$ locale charmap
UTF-8
No meu ambiente atual, o conjunto de caracteres é UTF-8, ou seja, caracteres são codificados com 1 a 4 bytes por caractere (embora a definição original de UTF-8 permitisse pontos de código de caractere até 0x7fffffff, a maioria das ferramentas reconhecer seqüências de byte UTF-8 de até 6 bytes).
Nesse conjunto de caracteres, todos os caracteres do Unicode estão disponíveis, um a
é codificado como valor de byte 65, um 乕
como os 3 bytes 228 185 149 e é
como a sequência de dois bytes 195 169 para exemplo.
$ printf 乕 | wc -mc
1 3
$ printf a | wc -mc
1 1
Agora:
$ export fr_FR.iso885915@euro
$ locale charmap
ISO-8859-15
Eu modifiquei meu ambiente, onde o conjunto de caracteres agora é ISO-8859-15 (outras coisas como idioma, símbolo de moeda, formato de data também foram modificadas, a coleção dessas configurações regionais sendo chamada de locale ). Eu preciso iniciar um novo emulador de terminal nesse ambiente para que ele adapte sua renderização de caracteres ao novo local.
ISO-8859-15 é um conjunto de caracteres de byte único, o que significa que ele possui apenas 256 caracteres (na verdade, até menos do que isso é realmente coberto). Esse conjunto de caracteres específico é usado para idiomas da Europa Ocidental, pois abrange a maioria de seus idiomas (e o símbolo do euro).
Possui o caractere a
com valor de byte 65, como em UTF-8 ou ASCII, também possui o caractere é
(como comumente usado em francês ou espanhol, por exemplo), mas com valor de byte 233, não t tem o caractere..
Nesse ambiente, wc -c
e wc -m
sempre fornecerão o mesmo resultado.
No Ubuntu, como na maioria dos sistemas modernos do tipo Unix, o padrão é geralmente UTF-8, pois é o único conjunto de caracteres suportado (e codificado) que cobre todo o intervalo Unicode.
Existem outras codificações de caracteres multi-byte, mas elas não são tão bem suportadas no Ubuntu e você tem que passar por aros para poder gerar uma localidade com elas, e se fizer isso, você encontrará muitas coisas não funciona corretamente.
Então, no Ubuntu, os conjuntos de caracteres são de byte único ou UTF-8.
Agora, mais algumas notas:
No UTF-8, nem todas as seqüências de bytes formam caracteres válidos. Por exemplo, todos os caracteres UTF-8 que não são ASCII são formados com bytes que possuem o 8º bit definido, mas onde apenas o primeiro possui o 7º bit definido.
Se você tiver uma seqüência de bytes com o conjunto de 8 bits, nenhum dos quais tem o 7º bit definido, então isso não pode ser traduzido para um caractere. E é aí que você começa a ter problemas e inconsistências, pois os softwares não sabem o que fazer com eles. Por exemplo:
$ printf '000' | wc -mc
0 3
$ printf '000' | grep -q . || echo no
no
wc
e grep
não encontram nenhum caractere lá, mas:
$ x=$'000' bash -c 'echo "${#x}"'
3
bash
encontra 3. Quando não é possível mapear uma seqüência de bytes para um caractere, ele considera cada byte como um caractere.
Pode ficar ainda mais complicado, pois há pontos de código em Unicode que são inválidos como caracteres, e alguns que são não-caracteres e, dependendo da ferramenta, sua codificação UTF-8 pode ou pode não ser considerado como um personagem.
Outra coisa a ser levada em consideração é a diferença entre personagem e grafite, e como eles são renderizados.
$ printf 'e\u301\u20dd\n'
é⃝
$ printf 'e\u301\u20dd' | wc -mc
3 6
Lá, codificamos 3 caracteres como 6 bytes renderizados como um grafo, porque temos 3 caracteres combinados (um caractere base, um acento agudo de combinação e um círculo delimitador de combinação).
A implementação GNU de wc
como encontrado no Ubuntu tem um -L
para informar a largura de exibição da linha mais larga na entrada:
$ printf 'e\u301\u20dd\n' | wc -L
1
Você também descobrirá que alguns caracteres ocupam 2 células nesse cálculo de largura, como o caractere 乕
acima:
$ echo 乕 | wc -L
2
Em conclusão: na palavra mais selvagem, byte, caractere e grafo não são necessariamente os mesmos.