Convertir entre les formulaires de normalisation Unicode sur la ligne de commande unix

22

En Unicode, certaines combinaisons de caractères ont plus d'une représentation.

Par exemple, le caractère ä peut être représenté par

  • "ä", c'est-à-dire le point de code U + 00E4 (deux octets c3 a4dans le codage UTF-8), ou
  • "ä", c'est-à-dire les deux points de code U + 0061 U + 0308 (trois octets 61 cc 88en UTF-8).

Selon la norme Unicode, les deux représentations sont équivalentes mais dans des "formes de normalisation" différentes, voir UAX # 15: Formes de normalisation Unicode .

La boîte à outils Unix contient toutes sortes d'outils de transformation de texte, sed , tr , iconv , Perl me viennent à l'esprit. Comment puis-je effectuer une conversion NF simple et rapide sur la ligne de commande?

glts
la source
2
On dirait qu'il existe un module "Unicode :: Normalization" pour perl qui devrait faire ce genre de chose: search.cpan.org/~sadahiro/Unicode-Normalize-1.16/Normalize.pm
goldilocks
@goldilocks si elle avait une CLI ... Je veux dire, je fais perl -MUnicode::Normalization -e 'print NFC(... euh ce qui vient ici maintenant
mirabilos

Réponses:

20

Vous pouvez utiliser l' uconvutilitaire d' ICU . La normalisation est obtenue par translittération ( -x).

$ uconv -x any-nfd <<<ä | hd
00000000  61 cc 88 0a                                       |a...|
00000004
$ uconv -x any-nfc <<<ä | hd
00000000  c3 a4 0a                                          |...|
00000003

Sur Debian, Ubuntu et autres dérivés, uconvest dans le libicu-devpackage. Sur Fedora, Red Hat et autres dérivés, et dans les ports BSD, c'est dans le icupackage.

Gilles 'SO- arrête d'être méchant'
la source
Cela fonctionne, merci. Vous devez cependant installer une bibliothèque de développeurs 30M à côté. Pire encore, je n'ai pas pu trouver la documentation appropriée pour uconv lui-même: où avez-vous trouvé any-nfd? Il semble que le développement de cet outil ait été abandonné, la dernière mise à jour date de 2005.
glts
2
@glts que j'ai trouvé any-nfden parcourant la liste affichée par uconv -L.
Gilles 'SO- arrête d'être méchant'
Sur Ubuntu, utilisez sudo apt install icu-devtoolspour exécuter uconv -x any-nfc, mais ne résolvez pas le problème le plus simple , par exemple un bugText.txt fichier avec "Iglésias, Bad-á, Good-á" converti en uconv -x any-nfc bugText.txt > goodText.txtrestant le même texte.
Peter Krauss
7

Python a un unicodedatamodule dans sa bibliothèque standard, qui permet de traduire les représentations Unicode via la unicodedata.normalize()fonction:

import unicodedata

s1 = 'Spicy Jalape\u00f1o'
s2 = 'Spicy Jalapen\u0303o'

t1 = unicodedata.normalize('NFC', s1)
t2 = unicodedata.normalize('NFC', s2)
print(t1 == t2) 
print(ascii(t1)) 

t3 = unicodedata.normalize('NFD', s1)
t4 = unicodedata.normalize('NFD', s2)
print(t3 == t4)
print(ascii(t3))

Exécution avec Python 3.x:

$ python3 test.py
True
'Spicy Jalape\xf1o'
True
'Spicy Jalapen\u0303o'

Python n'est pas bien adapté pour les liners shell one, mais cela peut être fait si vous ne voulez pas créer de script externe:

$ python3 -c $'import unicodedata\nprint(unicodedata.normalize("NFC", "ääääää"))'
ääääää

Pour Python 2.x, vous devez ajouter la ligne d'encodage ( # -*- coding: utf-8 -*-) et marquer les chaînes comme Unicode avec le caractère u:

$ python -c $'# -*- coding: utf-8 -*-\nimport unicodedata\nprint(unicodedata.normalize("NFC", u"ääääää"))'
ääääää
Nykakin
la source
3

Vérifiez-le avec l'outil hexdump:

echo  -e "ä\c" |hexdump -C 

00000000  61 cc 88                                          |a..|
00000003  

convertir avec iconv et vérifier à nouveau avec hexdump:

echo -e "ä\c" | iconv -f UTF-8-MAC -t UTF-8 |hexdump -C

00000000  c3 a4                                             |..|
00000002

printf '\xc3\xa4'
ä
mtt2p
la source
2
Cela ne fonctionne que sur macOS. Il n'y a pas d'utf-8-mac sous Linux, sur FreeBSD, etc. De plus, la décomposition en utilisant cet encodage ne suit pas la spécification (elle suit cependant l'algorithme de normalisation du système de fichiers macOS). Plus d'infos: search.cpan.org/~tomita/Encode-UTF8Mac-0.04/lib/Encode/…
antonone
@antonone pour être juste bien qu'aucun OS ne soit spécifié dans la question.
roaima
1
@roaima Oui, c'est pourquoi j'ai supposé que la réponse devrait fonctionner sur tous les systèmes basés sur Unix / Linux. La réponse ci-dessus ne fonctionne que sur macOS. Si vous cherchez une réponse spécifique à macOS, cela fonctionnera, en partie. Je voulais juste le souligner, parce que l'autre jour, j'ai perdu un peu de temps à me demander pourquoi je n'en ai pas utf-8-macsur Linux et si c'est normal.
antonone
3

Pour être complet, avec perl:

$ perl -CSA -MUnicode::Normalize=NFD -e 'print NFD($_) for @ARGV' $'\ue1' | uconv -x name
\N{LATIN SMALL LETTER A}\N{COMBINING ACUTE ACCENT}
$ perl -CSA -MUnicode::Normalize=NFC -e 'print NFC($_) for @ARGV' $'a\u301' | uconv -x name
\N{LATIN SMALL LETTER A WITH ACUTE}
Stéphane Chazelas
la source
2

coreutils a un patch pour obtenir un correct unorm. fonctionne très bien pour moi sur wchars 4 octets. suivez http://crashcourse.housegordon.org/coreutils-multibyte-support.html#unorm Le problème restant il y a des systèmes wchar à 2 octets (cygwin, windows, plus aix et solaris sur 32 bits), qui doivent transformer les points de code du haut les avions en paires de substitution et vice versa, et le libunistring / gnulib sous-jacent ne peut pas encore gérer cela.

perl a l' unicharsoutil, qui fait également les différentes formes de normalisation sur la ligne de commande. http://search.cpan.org/dist/Unicode-Tussle/script/unichars

rurban
la source