web-dev-qa-db-fra.com

commandes "wc -c" et "wc -m" sous linux

J'ai un fichier texte, son contenu est:

i k k

Lorsque j'utilise wc -m pour compter les nombres de caractères de ce fichier, le résultat est 7.

Question 1: Mais pourquoi ai-je 7, je ne devrais pas avoir "6" en supposant qu'il compte le caractère "fin de ligne"?

Question 2: Comment fonctionne exactement wc -m?

Question 3: Lorsque j'utilise wc -c (pour compter les nombres en octets), j'ai le même résultat que wc -m, donc à quoi sert-il d'avoir deux options différentes? Ils font exactement le même travail, n'est-ce pas? Si non, quelle est la différence et comment fonctionne wc -c?

24
SWIIWII

Vous ne devriez en effet y avoir que 6 caractères. Essayez de courir

cat -A filename

Pour voir les caractères non imprimables de votre fichier. Vous devez avoir quelque chose en plus. Si je fais un fichier comme le vôtre, je vois

i k k$

Avez-vous mis un espace? Cela ferait 7: i k k $ ou peut-être qu'il a une nouvelle ligne:

i k k$
$

qui est aussi 7

Comme tu dis

wc -m

compte les personnages et

wc -c

compte octets. Si tous vos caractères font partie du jeu de caractères ASCII, il n'y aura qu'un octet par caractère et vous obtiendrez le même nombre des deux commandes.

Essayez un fichier avec des caractères non ASCII:

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

Aha! Plus d'octets que de caractères maintenant.

36
Zanna
$ locale charmap
UTF-8

Dans mon environnement actuel, le jeu de caractères est UTF-8, c’est-à-dire que les caractères sont codés avec 1 à 4 octets par caractère (bien que, du fait que la définition originale du code de caractères UTF-8 autorisé pointe jusqu’à 0x7fffffff, la plupart des outils reconnaissent les caractères UTF- 8 séquences d’octets jusqu’à 6 octets).

Dans ce jeu de caractères, tous les caractères de l'Unicode sont disponibles, un aest codé en tant que valeur d'octet 65, un en tant que 3 octets 228 185 149 et é en tant que séquence d'octets 195 169 par exemple.

$ printf 乕 | wc -mc
  1       3
$ printf a | wc -mc
  1       1

À présent:

$ export fr_FR.iso885915@euro
$ locale charmap
ISO-8859-15

J'ai modifié mon environnement, où le jeu de caractères est maintenant ISO-8859-15 (d'autres éléments tels que la langue, le symbole monétaire, le format de la date ont également été modifiés, la collection de ces paramètres régionaux étant appelée locale ). Je dois démarrer un nouvel émulateur de terminal dans cet environnement pour que celui-ci puisse adapter le rendu de ses caractères à la nouvelle locale.

ISO-8859-15 est un jeu de caractères codé sur un octet, ce qui signifie qu'il ne compte que 256 caractères (en réalité, ils sont même moins couverts). Ce jeu de caractères particulier est utilisé pour les langues d'Europe occidentale, car il couvre la plupart de ses langues (et le symbole de l'euro).

Il a le caractère aavec la valeur d'octet 65, comme dans UTF-8 ou ASCII, ainsi que le caractère é (utilisé couramment en français ou en espagnol, par exemple), mais avec l'octet 233, il n'a pas le caractère 乕.

Dans cet environnement, wc -c et wc -m donneront toujours le même résultat.

Dans Ubuntu, comme sur la plupart des systèmes de type Unix modernes, la valeur par défaut est UTF-8, car il s’agit du seul jeu de caractères pris en charge (et de l’encodage) couvrant l’ensemble de la plage Unicode.

Il existe d’autres codages de caractères multi-octets, mais ils ne sont pas aussi bien supportés sous Ubuntu et vous devez effectuer des cercles pour pouvoir générer des paramètres régionaux avec ceux-ci. Si vous le faites, vous constaterez que beaucoup de choses ne le sont pas. travaille correctement.

Donc, dans Ubuntu, les jeux de caractères sont soit un octet, soit UTF-8.

Maintenant, quelques notes supplémentaires:

En UTF-8, toutes les séquences d'octets ne forment pas des caractères valides. Par exemple, tous les caractères UTF-8 qui ne sont pas ASCII sont formés avec des octets dont le huitième bit est défini, mais seul le premier est défini dans le septième bit.

Si vous avez une séquence d'octets avec le 8ème bit défini, aucun d'entre eux n'ayant le 7ème bit défini, cela ne peut pas être traduit en caractère. Et c'est lorsque vous commencez à avoir des problèmes et des incohérences, car les logiciels ne savent pas quoi faire avec ceux-ci. Par exemple:

$ printf '\200\200\200' | wc -mc
      0       3
$ printf '\200\200\200' | grep -q . || echo no
no

wcet grepn'y trouvent aucun caractère, mais:

$ x=$'\200\200\200' bash -c 'echo "${#x}"'
3

bashfind 3. Lorsqu'il ne peut pas mapper une séquence d'octets sur un caractère, il considère chaque octet comme un caractère.

Cela peut devenir encore plus compliqué car il existe des codes dans Unicode qui ne sont pas des caractères valides, et d'autres qui sont non-caractères , et selon l'outil, leur codage UTF-8 peut être considéré ou non comme un caractère.

Une autre chose à prendre en compte est la différence entre character et graphem et leur rendu.

$ printf 'e\u301\u20dd\n'
é⃝
$ printf 'e\u301\u20dd' | wc -mc
      3       6

Ici, nous codons 3 caractères sous forme de 6 octets restitués sous la forme d'un graphème, car nous avons 3 caractères combinés (un caractère de base, un accent aigu combinant et un cercle englobant).

L'implémentation GNU de wcname__, telle que trouvée sur Ubuntu, possède un commutateur -L qui vous indique la largeur d'affichage de la plus grande ligne de l'entrée:

$ printf 'e\u301\u20dd\n' | wc -L
1

Vous constaterez également que certains caractères occupent 2 cellules dans ce calcul de largeur, comme notre caractère ci-dessus:

$ echo 乕 | wc -L
2

En conclusion: dans le mot wilder, octet, caractère et graphem ne sont pas nécessairement identiques.

2
Stéphane Chazelas

La différence entre wc -c et wc -m réside dans le fait que, dans une langue avec des caractères multi-octets (disons, UTF8), la première compte en octets, tandis que la dernière compte en caractères. Considérons le fichier suivant:

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

(pour ceux qui ne parlent pas UTF8, ce sont les lettres 'x', 'y' et 'π' suivies d'une nouvelle ligne). C'est cinq octets de long:

$ wc -c dummy.txt 
5 dummy.txt

mais seulement quatre caractères:

$ wc -m dummy.txt 
4 dummy.txt
1
Mark