Преобразование форм нормализации Unicode в командной строке Unix

22

В Unicode некоторые комбинации символов имеют более одного представления.

Например, символ ä может быть представлен как

  • «ä», то есть кодовая точка U + 00E4 (два байта c3 a4в кодировке UTF-8) или как
  • «ä», то есть две кодовые точки U + 0061 U + 0308 (три байта 61 cc 88в UTF-8).

Согласно стандарту Unicode, эти два представления эквивалентны, но в разных «формах нормализации», см. UAX # 15: Формы нормализации Unicode .

В наборе инструментов Unix есть все виды инструментов преобразования текста , на ум приходят sed , tr , iconv , Perl. Как я могу быстро и легко конвертировать NF в командной строке?

glts
источник
2
Похоже, что для Perl существует модуль «Unicode :: Normalization», который должен делать такие вещи: search.cpan.org/~sadahiro/Unicode-Normalize-1.16/Normalize.pm
goldilocks
@goldilocks, если бы у него был CLI ... я имею в виду, я делаю perl -MUnicode::Normalization -e 'print NFC(... э-э, что здесь происходит сейчас ...
mirabilos

Ответы:

20

Вы можете использовать uconvутилиту от ICU . Нормализация достигается путем транслитерации ( -x).

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

На Debian, Ubuntu и других производных, uconvесть в libicu-devпакете. На Fedora, Red Hat и других производных, а также на портах BSD, это в icuпакете.

Жиль "ТАК - перестань быть злым"
источник
Это работает, спасибо. Вы должны установить 30M библиотеку разработчиков вместе с ней. Что еще хуже, я не смог найти нужную документацию для самого uconv: где ты нашел any-nfd? Похоже, что разработка этого инструмента была прекращена, последнее обновление было в 2005 году.
glts
2
@glts я нашел any-nfd, просматривая список, отображаемый uconv -L.
Жиль "ТАК - перестань быть злым"
В Ubuntu использовать sudo apt install icu-devtoolsдля запуска uconv -x any-nfc, но не решить простейшую проблему , например, bugText.txt файл с «Iglésias, Bad-á, Good-á», преобразованный с помощью uconv -x any-nfc bugText.txt > goodText.txtтого же текста.
Питер Краусс
7

Python имеет unicodedataмодуль в своей стандартной библиотеке, который позволяет переводить представления Unicode через unicodedata.normalize()функцию:

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

Запуск с Python 3.x:

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

Python плохо подходит для оболочек оболочки один, но это можно сделать, если вы не хотите создавать внешний скрипт:

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

Для Python 2.x необходимо добавить кодировку line ( # -*- coding: utf-8 -*-) и пометить строки как Unicode с символом u:

$ python -c $'# -*- coding: utf-8 -*-\nimport unicodedata\nprint(unicodedata.normalize("NFC", u"ääääää"))'
ääääää
Nykakin
источник
3

Проверьте это с помощью инструмента hexdump:

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

00000000  61 cc 88                                          |a..|
00000003  

преобразуйте с помощью iconv и проверьте снова с помощью hexdump:

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

00000000  c3 a4                                             |..|
00000002

printf '\xc3\xa4'
ä
mtt2p
источник
2
Это работает только на macOS. В Linux, на FreeBSD и т. Д. Отсутствует utf-8-mac. Кроме того, декомпозиция с использованием этой кодировки не соответствует спецификации (хотя и следует алгоритму нормализации файловой системы macOS). Дополнительная информация: search.cpan.org/~tomita/Encode-UTF8Mac-0.04/lib/Encode/…
antonone
@antonone, если быть честным, хотя в вопросе не указана ОС.
роайма
1
@roaima Да, именно поэтому я предположил, что ответ должен работать на всех системах, основанных на Unix / Linux. Ответ выше работает только на macOS. Если кто-то ищет ответ, специфичный для macOS, то он будет работать частично. Я просто хотел указать на это, потому что на днях я потерял некоторое время, размышляя, почему у меня нет utf-8-macLinux и это нормально.
антонон
3

Для полноты, с 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}
Стефан Шазелас
источник
2

Coreutils имеет патч, чтобы получить правильную unorm. прекрасно работает на 4-х байтовых wchars. следуйте http://crashcourse.housegordon.org/coreutils-multibyte-support.html#unorm . Остается проблема с 2-байтовыми системами wchar (cygwin, windows, плюс aix и solaris на 32-битных), которые должны преобразовывать кодовые точки из верхнего планы в суррогатные пары и наоборот, и лежащая в основе libunistring / gnulib пока не может справиться с этим.

В Perl есть unicharsинструмент, который также выполняет различные формы нормализации в cmdline. http://search.cpan.org/dist/Unicode-Tussle/script/unichars

rurban
источник
2

Существует утилита perl под названием Charlint, доступная с

https://www.w3.org/International/charlint/

который делает то, что вы хотите. Вам также придется скачать файл с

ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt

После первого запуска вы увидите, что Charlint жалуется на несовместимые записи в этом файле, поэтому вам придется удалить эти строки из UnicodeData.txt.

Захар Джо
источник