Converter entre formulários de normalização Unicode na linha de comando unix

22

No Unicode, algumas combinações de caracteres têm mais de uma representação.

Por exemplo, o caractere ä pode ser representado como

  • "ä", ou seja, o ponto de código U + 00E4 (dois bytes c3 a4na codificação UTF-8) ou como
  • "ä", ou seja, os dois pontos de código U + 0061 U + 0308 (três bytes 61 cc 88em UTF-8).

De acordo com o padrão Unicode, as duas representações são equivalentes, mas em diferentes "formas de normalização", consulte UAX # 15: Formulários de Normalização Unicode .

A caixa de ferramentas unix tem todos os tipos de ferramentas de transformação de texto: sed , tr , iconv , Perl. Como posso fazer uma conversão NF rápida e fácil na linha de comando?

glts
fonte
2
Parece que há um módulo de "Unicode :: normalização" para perl que deve fazer esse tipo de coisa: search.cpan.org/~sadahiro/Unicode-Normalize-1.16/Normalize.pm
Goldilocks
@goldilocks se tivesse um CLI ... Quer dizer, eu perl -MUnicode::Normalization -e 'print NFC(... er o que vem aqui agora ...
mirabilos

Respostas:

20

Você pode usar o uconvutilitário da UTI . A normalização é alcançada através da transliteração ( -x).

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

No Debian, Ubuntu e outros derivados, uconvestá no libicu-devpacote. No Fedora, Red Hat e outros derivados, e nas portas BSD, está no icupacote.

Gilles 'SO- parar de ser mau'
fonte
Isso funciona, obrigado. Você precisa instalar uma biblioteca dev de 30M ao lado dela. O que é pior: não consegui encontrar a documentação adequada para o próprio uconv: onde você encontrou any-nfd? Parece que o desenvolvimento desta ferramenta foi abandonada, última atualização foi em 2005.
MLDs
2
@glts que encontrei any-nfdnavegando pela lista exibida por uconv -L.
Gilles 'SO- stop be evil'
No Ubuntu, use sudo apt install icu-devtoolspara executar uconv -x any-nfc, mas não resolva o problema mais simples , por exemplo, um bugText.txt arquivo com "Iglésias, Bad-á, Good-á" convertido, uconv -x any-nfc bugText.txt > goodText.txtmantendo o mesmo texto.
Peter Krauss
7

O Python possui um unicodedatamódulo em sua biblioteca padrão, que permite converter representações Unicode através da unicodedata.normalize()função:

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))

Executando com o Python 3.x:

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

O Python não é adequado para liners de shell um, mas isso pode ser feito se você não quiser criar um script externo:

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

Para o Python 2.x, você deve adicionar a linha de codificação ( # -*- coding: utf-8 -*-) e marcar as strings como Unicode com o caractere u:

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

Verifique com a ferramenta hexdump:

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

00000000  61 cc 88                                          |a..|
00000003  

converta com iconv e verifique novamente com hexdump:

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

00000000  c3 a4                                             |..|
00000002

printf '\xc3\xa4'
ä
mtt2p
fonte
2
Isso funciona apenas no macOS. Não existe 'utf-8-mac' no Linux, no FreeBSDs, etc. Além disso, a decomposição usando essa codificação não segue a especificação (embora siga o algoritmo de normalização do sistema de arquivos do macOS). Mais informações: search.cpan.org/~tomita/Encode-UTF8Mac-0.04/lib/Encode/…
antonone
@antonone para ser justo, embora não houvesse SO especificado na pergunta.
roaima
1
@roaima Sim, é por isso que assumi que a resposta deve funcionar em todos os sistemas baseados em Unix / Linux. A resposta acima funciona apenas no macOS. Se alguém estiver procurando por uma resposta específica do macOS, funcionará, em parte. Eu só queria salientar isso, porque outro dia perdi um tempo me perguntando por que não tenho utf-8-macno Linux e se isso é normal.
antonone
3

Para completar, com 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
fonte
2

O coreutils possui um patch para obter o devido unorm. funciona bem para mim em wchars de 4 bytes. siga http://crashcourse.housegordon.org/coreutils-multibyte-support.html#unorm O problema restante existem sistemas wchar de 2 bytes (cygwin, windows, mais aix e solaris em 32 bits), que precisam transformar pontos de código da parte superior planos em pares substitutos e vice-versa, e o libunistring / gnulib subjacente ainda não pode lidar com isso.

O perl tem a unicharsferramenta, que também executa as várias formas de normalização no cmdline. http://search.cpan.org/dist/Unicode-Tussle/script/unichars

suburbano
fonte