Commande «wc -c» et «wc -m» sous linux


24

J'ai un fichier texte, son contenu est:

i k k

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

Question 1: Mais pourquoi ai-je obtenu 7, ne devrais-je pas obtenir " 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 d'octets), j'ai le même résultat que wc -m, alors quel est l'intérêt d'avoir deux options différentes ? Ils font exactement le même travail, non? Sinon, quelle est la différence et comment ça wc -cmarche?


1
Lisez Joel sur le minimum absolu du logiciel, chaque développeur de logiciels doit absolument, positivement, connaître Unicode et les jeux de caractères (sans excuses!) Pour obtenir des explications sur les caractères, l'encodage des caractères et les jeux de caractères
phuclv

1
Vous pourriez également avoir 7 si votre fichier provenait de Windows avec des fins de ligne CRLF
Chris H

Réponses:


36

Vous ne devez 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 crée 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 caractères et

wc -c

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

Essayez un fichier avec des caractères non ASCII:

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

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


3
J'ai utilisé la commande " cat -A " et j'ai enfin trouvé que j'avais un espace avant le caractère " fin de ligne " ( $ ). C'est pourquoi j'ai eu 7 au lieu de 6. Merci, le " chat -A " a beaucoup aidé.
SWIIWII

2
@SWIIWII Oui, je viens d'ajouter cela à ma réponse car je pensais que ce serait probablement ça :)
Zanna

1
le caractère de nouvelle ligne a également été compté. Même s'il est en quelque sorte non visible, c'est toujours un caractère et compte dans le fichier comme un bloc de données. Bon usage du chat -A d'ailleurs. Once pourrait également utiliser hexdump ou xxd pour faire de même
Sergiy Kolodyazhnyy

@Serg oui, et cat -Ale montrerait aussi. J'ai ajouté à ma réponse, merci :)
Zanna

@SWIIWII a mis du code entre guillemets `likethis`pour le rendre lisible, ne le
mettez

2
$ 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 parce que la définition d'origine de UTF-8 permette le code de caractère jusqu'à 0x7fffffff, la plupart des outils reconnaissent UTF- Séquences de 8 octets jusqu'à 6 octets).

Dans ce jeu de caractères, tous les caractères d'Unicode sont disponibles, a aest codé comme valeur d'octet 65, a comme 3 octets 228 185 149 et écomme séquence de deux 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 désormais ISO-8859-15 (d'autres choses comme la langue, le symbole monétaire, le format de date ont également été modifiés, la collection de ces paramètres régionaux étant appelée locale ). J'ai besoin de démarrer un nouvel émulateur de terminal dans cet environnement pour qu'il puisse adapter son rendu de caractère aux nouveaux paramètres régionaux.

ISO-8859-15 est un jeu de caractères à un octet, ce qui signifie qu'il n'a que 256 caractères (en fait encore moins que ceux qui sont réellement 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 acaractère avec la valeur d'octet 65 comme en UTF-8 ou ASCII, il a également le écaractère (comme couramment utilisé en français ou en espagnol par exemple) mais avec la valeur d'octet 233, il n'a pas le caractère 乕.

Dans cet environnement, wc -cet wc -mdonnera toujours le même résultat.

Dans Ubuntu, comme sur la plupart des systèmes modernes de type Unix, la valeur par défaut est généralement UTF-8 car c'est le seul jeu de caractères (et codage) pris en charge qui couvre toute la plage Unicode.

Il existe d'autres encodages de caractères multi-octets, mais ils ne sont pas aussi bien pris en charge sur Ubuntu et vous devez passer par des cercles pour pouvoir générer un environnement local avec ceux-ci, et si vous le faites, vous constaterez que beaucoup de choses ne le font pas travaille correctement.

Donc, en effet sur 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 des caractères ASCII sont formés avec des octets qui ont tous le 8e bit, mais où seul le premier a le 7e bit.

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

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

wcet grepn'y trouver aucun personnage mais:

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

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

Cela peut devenir encore plus compliqué car il y a des points de code dans Unicode qui ne sont pas valides en tant que caractères, et certains qui ne sont pas des caractères , et selon l'outil, leur codage UTF-8 peut ou non être considéré comme un caractère.

Une autre chose à prendre en considération est la différence entre le caractère et le graphem, et la façon dont ils sont rendus.

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

Là, nous avons codé 3 caractères sous forme de 6 octets rendus sous la forme d'un graphem, car nous avons 3 caractères combinés ensemble (un caractère de base, un accent aigu combinant et un cercle englobant combinant).

L'implémentation GNU de wccomme trouvée sur Ubuntu a un -Lcommutateur pour vous indiquer la largeur d'affichage de la ligne la plus large dans 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 le plus sauvage, l'octet, le caractère et le graphem ne sont pas nécessairement les mêmes.


1

La différence entre wc -cet wc -mest que dans un environnement local avec des caractères multi-octets (par exemple, UTF8), le premier compte les octets, tandis que le dernier compte les caractères. Considérez 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). Il est long de cinq octets:

$ wc -c dummy.txt 
5 dummy.txt

mais seulement quatre caractères:

$ wc -m dummy.txt 
4 dummy.txt

Ou, considérez même UTF-32 où chaque caractère a 4 octets.
Jörg W Mittag
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.