在UNIX命令行上在Unicode规范化形式之间进行转换


22

在Unicode中,某些字符组合具有多个表示形式。

例如,字符ä可以表示为

  • “ä”,即代码点U + 00E4(c3 a4采用UTF-8编码的两个字节),或者为
  • “ä”,即两个代码点U + 0061 U + 0308(61 cc 88UTF-8中为三个字节)。

根据Unicode标准,这两种表示形式是等效的,但是以不同的“规范化形式”,请参阅UAX#15:Unicode规范化形式

Unix工具箱提供了各种文本转换工具,包括sedtriconv,Perl。如何在命令行上快速轻松地进行NF转换?


2
似乎为perl提供了一个“ Unicode :: Normalization”模块,该模块应执行以下操作:search.cpan.org/~sadahiro/Unicode-Normalize-1.16/Normalize.pm
goldilocks 2013年

@goldilocks,如果它有一个CLI ...我的意思是,我知道了perl -MUnicode::Normalization -e 'print NFC(……呃现在发生了什么……
mirabilos

Answers:


20

您可以使用ICU中uconv实用程序。通过音译()实现标准化-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 2013年

2
any-nfd浏览了由显示的列表后发现了@glts uconv -L
吉尔斯(Gillles)“所以-不要再邪恶了”

在Ubuntu上,使用sudo apt install icu-devtoolsrun来运行uconv -x any-nfc,但不能解决最简单的问题,例如bugText.txt ,将“Iglésias,Bad-á,Good-á”转换uconv -x any-nfc bugText.txt > goodText.txt为相同文本的文件。
彼得·克劳斯

7

Python unicodedata在其标准库中具有模块,该模块允许通过以下unicodedata.normalize()功能转换Unicode表示形式:

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,您必须添加编码行(# -*- coding: utf-8 -*-),并使用u字符将字符串标记为Unicode:

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

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'
ä

2
这仅适用于macOS。在Linux,FreeBSD等上没有'utf-8-mac'。此外,使用此编码进行的分解不符合规范(尽管确实遵循macOS文件系统规范化算法)。更多信息:search.cpan.org/~tomita/Encode-UTF8Mac-0.04/lib/Encode/…–
antonone

@antonone是公平的,尽管在问题中未指定操作系统。
roaima

1
@roaima是的,这就是为什么我认为答案应该在所有基于Unix / Linux的系统上都有效的原因。上面的答案仅适用于macOS。如果您正在寻找特定于macOS的答案,那么它将部分起作用。我只是想指出这一点,因为前几天我浪费了一些时间,想知道为什么我没有utf-8-mac使用Linux,这是否正常。
安东尼

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。在4byte 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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.