“wc -c” e “wc -m” no linux

23

Eu tenho um arquivo de texto, seu conteúdo é:

i k k

Quando eu uso wc -m para contar números de caracteres neste arquivo, o resultado é 7 .

Pergunta 1: Mas por que consegui 7, não deveria ter " 6 " supondo que ele contasse com o caractere " fim-de-linha "? / p>

Pergunta 2: Como exatamente o wc -m funciona?

Pergunta 3: Quando uso wc -c (para contar números de bytes), tenho o mesmo resultado que wc -m , então qual é o ponto de ter duas opções diferentes ? Eles fazem exatamente o mesmo trabalho, não fazem? Se não, qual é a diferença e como o wc -c funciona?

    
por SWIIWII 28.07.2016 / 10:46

3 respostas

35

Você deve ter apenas 6 caracteres lá. Tente executar

cat -A filename

Para ver os caracteres não imprimíveis do seu arquivo. Você deve ter algo extra. Se eu fizer um arquivo como o seu, vejo

i k k$

Você colocou um espaço? Isso faria com 7: i k k $ ou talvez tenha uma nova linha:

i k k$
$

que também é 7

Como você diz

wc -m

conta caracteres e

wc -c

conta bytes. Se todos os seus personagens fizerem parte do conjunto de caracteres ASCII, haverá apenas 1 byte por caractere, assim você obterá a mesma contagem de ambos os comandos.

Experimente um arquivo com caracteres não ASCII:

$ echo ك > testfile
$ wc -m testfile
2 testfile
$ wc -c testfile
3 testfile

Aha! Mais bytes que caracteres agora.

    
por Zanna 28.07.2016 / 10:56
2
$ 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.

    
por Stéphane Chazelas 29.07.2016 / 09:27
1

A diferença entre wc -c e wc -m é que, em um código de idioma com caracteres multibyte (digamos, UTF8), o primeiro conta bytes, enquanto o último conta caracteres. Considere o seguinte arquivo:

$ hexdump -C dummy.txt 
00000000  78 79 cf 80 0a                                    |xy...|

(para quem não fala UTF8, são as letras 'x', 'y' e 'π', seguidas por uma nova linha). Tem cinco bytes de comprimento:

$ wc -c dummy.txt 
5 dummy.txt

mas apenas quatro caracteres:

$ wc -m dummy.txt 
4 dummy.txt
    
Comando
por Mark 28.07.2016 / 23:00