mt_*_*mt_ 11 linux locale posix utf-8 gnu-coreutils
我正在处理(希望)UTF-8文本的大文件.我可以使用Ubuntu 13.10(3.11.0-14-generic)和12.04重现它.
在调查一个bug我遇到了奇怪的行为
$ 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
Run Code Online (Sandbox Code Playgroud)
运行使用读取文件的自定义C++程序时也会出现重复项
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
Run Code Online (Sandbox Code Playgroud)
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=
Run Code Online (Sandbox Code Playgroud)
编辑:使用以下规范化后:
cat duplicates | uconv -f utf8 -t utf8 -x nfc > duplicates.nfc
Run Code Online (Sandbox Code Playgroud)
我仍然得到相同的结果
编辑:文件是有效的UTF-8根据iconv- (从这里)
$ iconv -f UTF-8 duplicates -o /dev/null
$ echo $?
0
Run Code Online (Sandbox Code Playgroud)
编辑:看起来像这样的东西: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依赖当前语言环境的不同行为的最小示例是:
$ 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
?
?
Run Code Online (Sandbox Code Playgroud)
显然,如果区域设置是en_US.UTF-8 uniq对待?和?重复,则不应该是这种情况.然后我再次运行相同的命令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 &
Run Code Online (Sandbox Code Playgroud)
唯一的区别是,与该版本LC_COLLATE=en_US.UTF-8名为strcoll()而LC_COLLATE=C没有这样做.所以我提出了以下最小例子strcoll():
#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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
?
?
0
-1
-10
-1
?
?
0
-1
-10
-1
Run Code Online (Sandbox Code Playgroud)
那么,这里有什么问题?为什么两个不同的字符strcoll()返回0(相等)?
| 归档时间: |
|
| 查看次数: |
5525 次 |
| 最近记录: |