我使用iconv库从使用UTF-8的现代输入源接口到使用Latin1的传统系统,即CP1252(ISO-8859-1的超集)。
该接口最近未能转换法语字符串"Éeducation",其中"É"被编码为十六进制45 CC 81
。请注意,目标编码确实有一个"É"字符,编码为C9
。
为什么iconv无法转换"É"?我检查了MacOS X 10.7.3提供的iconv命令行工具是否表示无法转换,并且PERL iconv模块也出现了故障。
更令人困惑的是,"É"字符的预编译形式(编码为C3 89
)转换得很好。
这是iconv的错误还是我错过了什么?
请注意,如果我尝试从UTF-16转换(其中"É"被编码为00 C9
组合或00 45 03 01
分解),我也会遇到同样的问题。
不幸的是,iconv确实不处理UTF-8中的分解字符,除了安装在Mac OS X上的版本。
处理Mac文件名时,可以将iconv与"utf8 Mac"字符集选项一起使用。它还考虑了Mac分解形式的一些特性
然而,iconv或libiconv的非mac版本不支持这一点,我找不到mac上使用的提供这种支持的来源。
我同意你的观点,即iconv应该能够处理NFC和NFD形式的UTF8,但在有人修补源之前,我们必须手动检测并处理它,然后再将内容传递给iconv。
面对这个烦人的问题,我使用了Jukka建议的Perl的Unicode::Normalize模块。
#!/usr/bin/perl
use Encode qw/decode_utf8 encode_utf8/;
use Unicode::Normalize;
while (<>) {
print encode_utf8( NFC(decode_utf8 $_) );
}
在调用iconv之前使用规范化器(在本例中,为规范化表单C)。
一个处理字符编码(字符的不同表示,或者更确切地说,代码点,作为字节序列)和在它们之间转换的程序应该将预编译和组合的形式视为不同的。分解的É是两个代码点,因此与预编译的É不同,后者是一个代码点。