我正在处理(我希望)UTF-8文本巨大的文件。我可以使用Ubuntu 13.10(3.11.0-14-generic)和12.04复制它。
在调查错误时,我遇到了奇怪的行为
$ export LC_ALL=en_US.UTF-8 $ sort part-r-00000 | uniq -d ɥ ɨ ɞ ɧ 251 ɨ ɡ ɞ ɭ ɯ 291 ɢ ɫ ɬ ɜ 301 ɪ ɳ 475 ʈ ʂ 565 $ export LC_ALL=C $ sort part-r-00000 | uniq -d $ # no duplicates found
当运行使用来读取文件的自定义C ++程序时,重复项也会出现std::stringstream-由于使用en_US.UTF-8区域设置时重复项而导致失败。 至少对于std::string输入和输出,C ++似乎并不受影响。
std::stringstream
en_US.UTF-8
std::string
为什么在使用UTF-8语言环境时发现重复项,而在C语言环境中找不到重复项?
语言环境对导致此行为的文本进行了哪些转换?
编辑:这是一个小例子
$ uniq -D duplicates.small.nfc ɢ ɦ ɟ ɧ ɹ 224 ɬ ɨ ɜ ɪ ɟ 224 ɥ ɨ ɞ ɧ 251 ɯ ɭ ɱ ɪ 251 ɨ ɡ ɞ ɭ ɯ 291 ɬ ɨ ɢ ɦ ɟ 291 ɢ ɫ ɬ ɜ 301 ɧ ɤ ɭ ɪ 301 ɹ ɣ ɫ ɬ 301 ɪ ɳ 475 ͳ ͽ 475 ʈ ʂ 565 ˈ ϡ 565
locale问题出现时的输出:
locale
$ locale LANG=en_US.UTF-8 LC_CTYPE="en_US.UTF-8" LC_NUMERIC=de_DE.UTF-8 LC_TIME=de_DE.UTF-8 LC_COLLATE="en_US.UTF-8" LC_MONETARY=de_DE.UTF-8 LC_MESSAGES="en_US.UTF-8" LC_PAPER=de_DE.UTF-8 LC_NAME=de_DE.UTF-8 LC_ADDRESS=de_DE.UTF-8 LC_TELEPHONE=de_DE.UTF-8 LC_MEASUREMENT=de_DE.UTF-8 LC_IDENTIFICATION=de_DE.UTF-8 LC_ALL=
编辑:规范化后使用:
cat duplicates | uconv -f utf8 -t utf8 -x nfc > duplicates.nfc
我仍然得到相同的结果
编辑:文件根据iconv-(从此处开始)是有效的UTF-8
iconv
$ iconv -f UTF-8 duplicates -o /dev/null $ echo $? 0
编辑:看起来与此类似:http : //xahlee.info/comp/unix_uniq_unicode_bug.html 和 https://lists.gnu.org/archive/html/bug- coreutils/2012-07/msg00072.html
它在FreeBSD上工作
我将问题归结为strcoll()函数问题,该问题与Unicode规范化无关。回顾:我的最小示例展示了uniq依赖当前语言环境的不同行为:
strcoll()
uniq
$ echo -e "\xc9\xa2\n\xc9\xac" > test.txt $ cat test.txt ɢ ɬ $ LC_COLLATE=C uniq -D test.txt $ LC_COLLATE=en_US.UTF-8 uniq -D test.txt ɢ ɬ
显然,如果语言环境是en_US.UTF-8 uniq对待ɢ并ɬ作为重复项,则情况并非如此。然后,我再次使用运行相同的命令,valgrind并使用来调查了两个调用图kcachegrind。
ɢ
ɬ
valgrind
kcachegrind
$ LC_COLLATE=C valgrind --tool=callgrind uniq -D test.txt $ LC_COLLATE=en_US.UTF-8 valgrind --tool=callgrind uniq -D test.txt $ kcachegrind callgrind.out.5754 & $ kcachegrind callgrind.out.5763 &
唯一的区别是,与该版本LC_COLLATE=en_US.UTF-8名为strcoll()而LC_COLLATE=C没有这样做。因此,我提出了以下最小示例strcoll():
LC_COLLATE=en_US.UTF-8
LC_COLLATE=C
#include <iostream> #include <cstring> #include <clocale> int main() { const char* s1 = "\xc9\xa2"; const char* s2 = "\xc9\xac"; std::cout << s1 << std::endl; std::cout << s2 << std::endl; std::setlocale(LC_COLLATE, "en_US.UTF-8"); std::cout << std::strcoll(s1, s2) << std::endl; std::cout << std::strcmp(s1, s2) << std::endl; std::setlocale(LC_COLLATE, "C"); std::cout << std::strcoll(s1, s2) << std::endl; std::cout << std::strcmp(s1, s2) << std::endl; std::cout << std::endl; s1 = "\xa2"; s2 = "\xac"; std::cout << s1 << std::endl; std::cout << s2 << std::endl; std::setlocale(LC_COLLATE, "en_US.UTF-8"); std::cout << std::strcoll(s1, s2) << std::endl; std::cout << std::strcmp(s1, s2) << std::endl; std::setlocale(LC_COLLATE, "C"); std::cout << std::strcoll(s1, s2) << std::endl; std::cout << std::strcmp(s1, s2) << std::endl; }
输出:
ɢ ɬ 0 -1 -10 -1 � � 0 -1 -10 -1
那么,这怎么了?为什么对两个不同的字符strcoll()返回0(等于)?
0