Vous ne pouvez pas utiliser `cut -c` (` --characters`) avec UTF-8?

15

La commande cuta une option -cpour travailler sur des caractères, au lieu d'octets avec l'option -b. Mais cela ne semble pas fonctionner, en en_US.UTF-8locale:

Le deuxième octet donne le deuxième caractère ASCII (qui est codé de la même manière en UTF-8):

$ printf 'ABC' | cut -b 2          
B

mais ne donne pas le deuxième des trois caractères grecs non ASCII dans les paramètres régionaux UTF-8:

$ printf 'αβγ' | cut -b 2         
�

C'est bien - c'est le deuxième octet .
Nous regardons donc le deuxième caractère à la place:

$ printf 'αβγ' | cut -c 2 
�

Ça a l'air cassé.
Avec quelques expériences, il s'avère que la plage 3-4montre le deuxième caractère:

$ printf 'αβγ' | cut -c 3-4
β

Mais c'est exactement la même chose que les octets 3 à 4:

$ printf 'αβγ' | cut -b 3-4
β

Donc, -cne fait pas plus que -bpour UTF-8.

Je m'attendrais à ce que la configuration locale ne soit pas correcte pour UTF-8, mais en comparaison, wcfonctionne comme prévu;
Il est souvent utilisé pour compter les octets, avec l'option -c( --bytes). (Notez les noms d'options confus.)

$ printf 'αβγ' | wc -c
6

Mais il peut également compter les caractères avec l'option -m( --chars), qui fonctionne simplement:

$ printf 'αβγ' | wc -m
3

Ma configuration semble donc correcte - mais quelque chose de spécial cut.

Peut-être qu'il ne prend pas du tout en charge l'UTF-8? Mais il semble prendre en charge les caractères multi-octets, sinon il n'aurait pas besoin de prendre en charge -bet -c.

Alors, qu'est-ce qui ne va pas? Et pourquoi?


La configuration locale semble correcte pour utf8, pour autant que je sache:

$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

L'entrée, octet par octet:

$ printf 'αβγ' | hd 
00000000  ce b1 ce b2 ce b3                                 |......|
00000006
Volker Siegel
la source
Intéressant! Il semble -cutiliser le même code que -b. Avez-vous regardé le code source? Vous pouvez peut-être trouver un indice de ce à quoi il -cest réellement destiné.
michas

Réponses:

13

Vous n'avez pas dit lequel cutvous utilisez, mais puisque vous avez mentionné l'option longue GNU, --charactersje suppose que c'est celle-là. Dans ce cas, notez ce passage deinfo coreutils 'cut invocation' :

‘-c character-list’
‘--characters=character-list’

Sélectionnez pour imprimer uniquement les caractères aux positions répertoriées dans la liste des caractères. La même chose que -bpour l'instant , mais l'internationalisation changera cela.

(pas d'italique dans l'original)

Pour le moment, GNU cutfonctionne toujours en termes de "caractères" à un octet, donc le comportement que vous voyez est attendu.


POSIX prend en charge à la fois les options -bet - elles n'ont pas été ajoutées à GNU car il avait une prise en charge multi-octets et elles fonctionnaient correctement, mais pour éviter de donner des erreurs sur les entrées compatibles POSIX. La même chose a été faite dans certaines autres implémentations, mais pas pour FreeBSD et OS X au moins.-ccut-ccut

C'est le comportement historique de -c. -ba été récemment ajouté pour prendre en charge le rôle d'octets afin de -cpouvoir fonctionner avec des caractères multi-octets. Peut-être que dans quelques années, cela fonctionnera comme souhaité de manière cohérente, bien que les progrès n'aient pas été rapides (cela fait déjà plus d'une décennie). GNU cut n'implémente même pas encore cette -noption , même si elle est orthogonale et destinée à faciliter la transition. Il existe des problèmes de compatibilité potentiels avec les anciens scripts, ce qui peut être un problème, bien que je ne sache pas définitivement quelle est la raison.

Michael Homer
la source
1
bon travail. vous trouverez également le même genre de commentaires dans les trdocuments de GNU . et même tarsi je ne me souviens pas. je suppose que c'est un gros projet.
mikeserv
Existe-t-il une solution de contournement pour le problème Unicode cut? Par exemple, où est-il possible de télécharger les sources pour patché cut? Ou serait-il plus facile d'utiliser un autre utilitaire? (la grepsolution ci-dessous ne fonctionne pas correctement avec les plages, par exemple 5-8,44-49)
dma_k
voir cet article de 2017, sous-titré «Notes aléatoires et pointeurs concernant l'effort en cours pour ajouter le support multi-octets et unicode dans GNU Coreutils» : crashcourse.housegordon.org/coreutils-multibyte-support.html
myrdd
vous pouvez trouver des alternatives cut -cici: superuser.com/questions/506164/…
myrdd
5

colrm(une partie de util-linux, devrait déjà être installé sur la plupart des distributions) semble gérer l'internationalisation beaucoup mieux:

$ echo 'αβγ' | colrm 3
αβ
$ echo 'αβγ' | colrm 2
α

Méfiez-vous de la numérotation: colrm Nsupprimera les colonnes de N, en imprimant les caractères jusqu'à N-1.

( crédits )

Skippy le Grand Gourou
la source
2

Étant donné que de nombreuses grepimplémentations sont compatibles avec plusieurs octets, vous pouvez également utiliser grep -opour simuler certaines utilisations de cut -c.

$ echo Τηεοδ29 | grep -o '^..'
Τη
$ echo Τηεοδ29 | egrep -o '^..' | grep -o '.$'
η

Ajustez le nombre de périodes pour simuler des cutplages.

Royce Williams
la source