Minha pergunta é: Por que não há um caractere "delimitador" específico? Um que seria usado para todos os tipos de delimitação. Temos caracteres especiais para nova linha, configurações de impressão, etc...
Por que às vezes usamos vírgulas, espaços, tabulações etc. se esses são caracteres de texto comuns. Existe história por trás disso? Como talvez eles não precisassem de caracteres delimitadores quando ASCII ou algo parecido foi feito?
(O que parece fazer sentido para mim: ter um caractere delimitador especial que tenha como único objetivo "delimitar" valores separados quando necessário)
Delimitadores já existem em ASCII . Decimais 28-31 (hex 1C-1F) são delimitadores. Isso inclui os separadores de arquivo, registro, grupo e unidade.
Eu diria que não os usamos, pois é mais fácil digitar caracteres do teclado que não exigem várias teclas para digitar um único caractere. Isso também permite um intercâmbio mais fácil entre diferentes formatos. Os valores separados por vírgula funcionarão em praticamente qualquer sistema, compatível com ASCII ou não.
Como já observado, ASCII inclui delimitadores. O problema não é que uma chave extra é necessária durante a entrada de dados para incluir os delimitadores - Control não é mais difícil de usar do que Shift para maiúsculas ou outro caractere especial imprimível (por exemplo, !@#$). O problema é que tradicionalmente esses caracteres de controle não são diretamente visíveis . Mesmo tabulação, retorno de carro e alimentação de linha - que produzem ações imediatas , não produzem saída visível.
Você não pode dizer a diferença em um teletipo entre tabulações e espaços. Você não pode dizer a diferença entre feeds de linha e espaços para fim de linha + quebra para próxima linha. Da mesma forma, os delimitadores não possuem uma imagem imprimível definida. Eles podem aparecer em alguns editores de texto (modernos) e podem produzir ações imediatas em vários dispositivos, mas não deixam marca.
Tudo isso não importa se os dados são projetados apenas para serem legíveis por máquina - ou seja, o que comumente chamamos de arquivos binários . Mas o texto para entrada e transferência de dados entre sistemas é muitas vezes, deliberadamente, legível por humanos. Se for legível por humanos, os delimitadores precisam ser imprimíveis.
Como foi mencionado em outra resposta, o ASCII possui delimitadores. Olhando aqui [1], estes são mencionados:
e estes são usados. Por exemplo, U+001C (octal 34), é a string padrão
SUBSEP
[2] para GNU AWK.Isso é principalmente histórico.
Nos velhos tempos da informática, os arquivos de dados eram principalmente arquivos de campos de largura fixa porque era o IO natural para linguagens como Fortran IV e COBOL: n caracteres para o primeiro campo, m para o segundo, etc.
Em seguida, a linguagem C forneceu uma
scanf
função que dividia a entrada em (conjuntos de) espaços em branco, e as pessoas começaram a usar o formato livre para arquivos de dados contendo números. Mas isso leva a resultados confusos quando alguns campos podem conter espaços (scanf
é conhecido como analisador de pobre ). Então, como a outra função padrão para divisão erastrtok
que usava um único delimitador, a maioria das pessoas (falando inglês) começou a usar a vírgula (,
) como separador, porque é fácil escrever manualmente um arquivo de valor separado por vírgula em um editor de texto.Então o suporte a idiomas nacionais entrou no jogo... Em alguns idiomas europeus (francês), o ponto decimal é a vírgula. Os caras de TI estavam acostumados com o ponto decimal, mas menos técnicos não estavam, então as versões francesas do Windows começaram a definir o ponto e vírgula (
;
) como o separador para permitir a vírgula em números decimais.Enquanto isso, alguns perceberam que quando os campos sempre tinham tamanho próximo, um caractere de tabulação (que existia em todos os teclados) permitia fornecer um bom alinhamento vertical e esse era o motivo de um terceiro padrão.
Finalmente, a padronização começou a ser um fato, e a RFC 4180 surgiu em 2005. Ela definiu a vírgula como o separador oficial, mas como o Windows decidiu jogar o jogo NLS, ferramentas e bibliotecas que queriam processar arquivos reais tiveram que se adaptar vários delimitadores possíveis.
E é por isso que em 2021, temos muitos delimitadores possíveis em arquivos CSV...
Acontece que existe um delimitador universal de fato em ASCII: o caractere nulo. O Unix e a linguagem C mostraram que é possível construir uma plataforma inteira na qual o caractere nulo é banido das cadeias de caracteres, servindo como terminador em sua representação. Outras plataformas seguiram o exemplo, como o Microsoft Windows.
Hoje, é uma garantia praticamente rígida de que nenhum dado textual contém um byte nulo. Se um dado contém um byte nulo, é binário e não texto.
Se você quiser armazenar uma sequência de registros textuais ou campos em um fluxo de bytes, se você separá-los com nulos, não terá quase nenhum problema. Nulos não requerem nenhum absurdo como escapar. Se alguém aparecer e disser que quer incluir um byte nulo em um campo de texto, você pode rir dele como um comediante.
Exemplos de separação nula na natureza:
A Microsoft permite que os itens no registro sejam multi-strings: itens únicos contendo várias strings. Isso é armazenado como uma sequência de strings terminadas em nulo catenizadas juntas, com um byte nulo extra para encerrar a sequência inteira. Como
"the\0quick\0brown\0fox\0\0"
para representar a lista de strings"the"
,"quick"
,"brown"
,"fox"
.No kernel Linux, as variáveis de ambiente de cada processo estão disponíveis através do
/proc
sistema de arquivos como/proc/<pid>/environ
. Este arquivo virtual usa separação nula, comoPATH=/bin:/usr/bin\0TERM=xterm\0...
.Alguns utilitários GNU têm a opção de produzir saída separada por nulos, e é precisamente isso que permite que eles sejam usados para escrever scripts muito mais robustos. GNU
find
tem um-print0
predicado para imprimir caminhos com terminação nula em vez de separação de nova linha. Esses caminhos podem ser alimentados para osxargs -0
quais lê strings separadas por nulo de sua entrada padrão e as transforma em argumentos de linha de comando para um comando especificado. Esta combinação passará de forma limpa absolutamente todos os nomes/caminhos de arquivo, independentemente do que eles contenham: porque os caminhos não podem conter um byte nulo.Por que fazemos jogos com outra separação? Tabs, vírgulas, ponto e vírgula e outros enfeites, em vez de apenas usar null? O problema é que precisamos de vários níveis de separação. Ok, então os nulos cortam o fluxo de bytes em textos, de forma confiável. Mas dentro desses textos, pode haver outro nível de delimitação necessário. Às vezes acontece que uma única string tem mais estrutura dentro dela. Um caminho contém barras para separar componentes. Um endereço MAC usa dois pontos para separar bytes. Esse tipo de coisas. Um endereço de e-mail tem vários níveis de delimitação aninhada, como
local@domain
ao redor do@
símbolo e, em seguida, a parte do domínio separada por pontos. Parênteses são permitidos lá e coisas como%
e!
. As pessoas escrevem código de manipulação de strings para lidar com esses formatos, e esse código de manipulação de strings não gosta de bytes nulos em muitas linguagens, devido à influência de C e Unix.Demonstração do GNU Awk usando o byte nulo como separador de campo, processando arquivos
/proc/self/environ
.Obtemos um campo extra em branco devido ao byte nulo no final, porque o Awk o está tratando como um separador de campo, em vez de terminador. No entanto, isso é possível precisamente porque o GNU Awk permite que o byte nulo seja um constituinte das cadeias de caracteres. O argumento
-F '\0'
não precisa funcionar, de acordo com a especificação POSIX. POSIX diz, em uma tabela intitulada "Escape Sequences in awk" quePortanto, é totalmente não-portável contar com o Awk para separar campos ou registros no byte nulo. Esse tipo de problema de linguagem é provavelmente uma razão pela qual não fazemos mais uso de caracteres nulos.
Para iluminar um pouco mais a história dada por @SergeBallesta, nos dias iniciais do ASCII recém-adotado (estamos falando de mainframe), o objetivo geral era padronizar códigos de entrada entre sistemas para que todos estivessem na mesma página. Havia muita disputa entre os fabricantes de sistemas para manter seus produtos proprietários (essencialmente, as coisas sendo utilizáveis apenas em seus sistemas) e isso era prejudicial à portabilidade. Esse problema estava relacionado principalmente a levar um programa, ou entrada e saída, de um sistema para outro. Por exemplo, pode-se escrever uma fita de saída com arquivos de entrada de dados, alguns arquivos de saída e alguns arquivos de programa FORTRAN que foram usados em um sistema, levar essa fita para outro sistema feito por um fabricante diferente e descobrir que a fita não era legível . Os grandes da sala, a IBM, tinham uma boa plataforma padronizada, EBCDIC, que era adaptável como ASCII com apenas uma pequena alteração na codificação binária do conjunto de caracteres EBCDIC. Todos aderiram a isso. Até então, o único conjunto de caracteres padronizado estava em ummáquina de escrever!
No entanto, na fazenda, a programação estava amplamente relacionada à simples leitura de dados de entrada que estavam em um formato determinado pelo programador, manipulação desses dados em um programa e produção de uma saída que também estava em um formato determinado pelo programador. Não houve necessidade de delimitador. Um dos usos mais intensivos de entrada e saída formatada foi com a linguagem de programação FORTRAN. Por exemplo, os dados seriam digitados em cartões Hollerith de 80 colunas em um formato de entrada específico e organizado, determinado pela pessoa que programou esse segmento de entrada do programa. Tudo foi formatado, padronizado, desenhado pelo programador/usuário. Não havia separação por vírgula dos dados de entrada. A saída foi impressa em folha larga de 132 colunas, papel perfurado na borda. A saída também foi perfurada em cartões de 80 colunas. A entrada e a saída de formato padronizado não exigiam um delimitador para separar os dados de entrada. Tudo foi bem tabulado. Os dados de entrada foram separados por tabulação; a saída impressa estava em colunas tabuladas com títulos, tudo bonito e arrumado.
Deve-se lembrar que em FORTRAN, todos os aspectos do controle do carro de impressão eram possíveis para o programador. Na verdade, o programador foi totalmente responsável por entender como operar uma impressora de 132 colunas a partir de um programa FORTRAN, bem como representar a saída na memória do computador para uma saída eficiente para uma impressora, ou para fita, onde o referido arquivo poderia estar vivo. -impresso, revisado em um terminal ou impresso posteriormente.
Com o advento da computação desktop personalizada, tudo isso mudou porque a entrada e a saída de dados se tornaram totalmente eletrônicas. Sim, ainda havia arquivos de entrada e saída formatados, mas o ambiente de programação ficou mais interativo com as necessidades do usuário. Um arquivo com entrada formatada como imagens de cartões de 80 colunas, ou páginas de saída de 132 colunas, pode ser lido cartão por cartão ou linha por linha por um pré-processador de entrada (uma sub-rotina), delimitado por vírgulas procurando por trilhas espaços em branco na entrada necessária e regravados em um arquivo temporário na memória. A entrada de cópia impressa formatada compatível com FORTRAN não foi necessária. Tudo isso foi muito fácil com um conjunto de caracteres padronizado, e o ASCII tornou essa padronização possível. Na verdade, a chave para fazer isso, ovírgula, já estava em ASCII. O arquivo temporário poderia então ser relido por um software que usasse especificamente a entrada de arquivos de dados delimitados por vírgulas . Agora tudo mudou! Apenas alguns poucos anos foram necessários para relegar o mainframe à história e avançar o estado da arte para um plano totalmente novo.