Comment faire en sorte que les caractères non ascii (unicode) soient pris en compte?

36

J'essaie de supprimer certains caractères du fichier (UTF-8). J'utilise trà cette fin:

tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat 

Le fichier contient des caractères étrangers (comme "Латвийская" ou "àé"). trne semble pas les comprendre: il les traite comme des non-alpha et les supprime aussi.

J'ai essayé de modifier certains de mes paramètres régionaux:

LC_CTYPE=C LC_COLLATE=C tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat
LC_CTYPE=ru_RU.UTF-8 LC_COLLATE=C tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat
LC_CTYPE=ru_RU.UTF-8 LC_COLLATE=ru_RU.UTF-8 tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat

Malheureusement, rien de tout cela n'a fonctionné.

Comment puis-je faire trcomprendre Unicode?

MatthewRock
la source

Réponses:

29

C'est une limitation connue ( 1 , 2 , 3 , 4 , 5 , 6 ) de la mise en oeuvre GNU de tr.

Ce n'est pas tant qu'il ne prend pas en charge les caractères étrangers , non anglais ou non ASCII, mais qu'il ne prend pas en charge les caractères multi-octets.

Ces caractères cyrilliques seraient traités correctement, s’ils étaient écrits dans le jeu de caractères iso8859-5 (codet sur un octet par caractère) (et que votre paramètre régional utilisait ce jeu de caractères), mais votre problème est que vous utilisez le format UTF-8 où les paramètres non-ASCII sont utilisés. les caractères sont codés sur 2 octets ou plus.

GNU a un plan (voir aussi ) pour résoudre ce problème et le travail est en cours, mais pas encore.

FreeBSD ou Solaris trn'ont pas le problème.


En attendant, dans la plupart des cas d'utilisation de tr, vous pouvez utiliser GNU sed ou GNU awk qui prennent en charge les caractères multi-octets.

Par exemple, votre:

tr -cs '[[:alpha:][:space:]]' ' '

pourrait être écrit:

gsed -E 's/( |[^[:space:][:alpha:]])+/ /'

ou:

gawk -v RS='( |[^[:space:][:alpha:]])+' '{printf "%s", sep $0; sep=" "}'

Pour convertir entre les majuscules et les minuscules ( tr '[:upper:]' '[:lower:]'):

gsed 's/[[:upper:]]/\l&/g'

(c'est lune minuscule L, pas le 1chiffre).

ou:

gawk '{print tolower($0)}'

Pour la portabilité, perlest une autre alternative:

perl -Mopen=locale -pe 's/([^[:space:][:alpha:]]| )+/ /g'
perl -Mopen=locale -pe '$_=lc$_'

Si vous savez que les données peuvent être représentées dans un jeu de caractères d'un octet, vous pouvez les traiter dans ce jeu de caractères:

(export LC_ALL=ru_RU.iso88595
 iconv -f utf-8 |
   tr -cs '[:alpha:][:space:]' ' ' |
   iconv -t utf-8) < Russian-file.utf8
Stéphane Chazelas
la source
1
J'ai accepté votre question en raison d'informations sur tr. J'ai résolu le problème et enlevé la question de savoir comment le résoudre (ainsi, les personnes à la recherche de tr ne trouveront que des informations sur celui-ci, et non un problème arbitraire). Si vous pouviez également supprimer la solution, elle ne serait plus nécessaire, je vous en serais reconnaissant.
MatthewRock
3
@ MatthewRock Je l'ai conservé, mais il a été reformulé et rendu plus générique, car donner un mot serait utile aux personnes ayant le même problème.
Stéphane Chazelas
Où avez-vous une idée du fait que cyrillique est (habituellement) codé selon ISO 8859-5? Avez-vous déjà vu un texte en russe dans un format autre que Unicode?
Incnis Mrsi
9
@IncnisMrsi, tout ce qui compte ici est que l'ISO 8859-5 soit l'un de ces jeux de caractères à un octet qui comporte ces caractères cyrilliques. Qu'il soit répandu ou non est sans importance ici. Si vous avez des paramètres régionaux avec KOI-R ou un jeu de caractères window-1251, utilisez-les plutôt.
Stéphane Chazelas
@IncnisMrsi Le russe sur le Web est presque toujours encodé en UTF-8 (ou occasionnellement dans Windows-1251), mais uniquement parce que nous avons très tôt ressenti la douleur de nombreux encodages à un octet. Voici une ancienne page Web (environ 1998) avec un commutateur de codage (non fonctionnel): sch57.ru/collect .
Alex Shpilkin