一尘不染

语言环境如何在Linux / POSIX中工作,并且应用了哪些转换?

linux

我正在处理(我希望)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 ++似乎并不受影响。

为什么在使用UTF-8语言环境时发现重复项,而在C语言环境中找不到重复项?

语言环境对导致此行为的文本进行了哪些转换?

编辑:是一个小例子

$ uniq -D duplicates.small.nfc 
ɢ ɦ ɟ ɧ ɹ       224
ɬ ɨ ɜ ɪ ɟ       224
ɥ ɨ ɞ ɧ 251
ɯ ɭ ɱ ɪ 251
ɨ ɡ ɞ ɭ ɯ       291
ɬ ɨ ɢ ɦ ɟ       291
ɢ ɫ ɬ ɜ 301
ɧ ɤ ɭ ɪ 301
ɹ ɣ ɫ ɬ 301
ɪ ɳ     475
ͳ ͽ     475
ʈ ʂ     565
ˈ ϡ     565

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 -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上工作


阅读 312

收藏
2020-06-07

共1个答案

一尘不染

我将问题归结为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
ɢ
ɬ

显然,如果语言环境是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 &

唯一的区别是,与该版本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;
}

输出:

ɢ
ɬ
0
-1
-10
-1

�
�
0
-1
-10
-1

那么,这怎么了?为什么对两个不同的字符strcoll()返回0(等于)?

2020-06-07