为什么UTF-8文本在OS X和Linux之间以不同的顺序排序?

And*_* H. 10 linux sorting macos utf-8

我有一个包含UTF-8编码文本行的文本文件:

mac-os-x$ cat unsorted.txt
?
foo
?
'foo'
?
Run Code Online (Sandbox Code Playgroud)

如果它有助于重现问题,这里有一个校验和和文件中确切字节的转储,以及如何自己生成文件(在Linux上,base64 -d代替使用-D):

mac-os-x$ shasum unsorted.txt
a6d0b708d3e0cafb0c6e1af7450e9243da8cb078  unsorted.txt

mac-os-x$ perl -ne 'print join(" ", map { sprintf "%02x", ord } split //), "\n"' unsorted.txt
e3 82 a6 0a
66 6f 6f 0a
e3 83 81 0a
27 66 6f 6f 27 0a
e6 b4 a5 0a

mac-os-x$ echo 44KmCmZvbwrjg4EKJ2ZvbycK5rSlCg== | base64 -D > unsorted.txt
Run Code Online (Sandbox Code Playgroud)

当我在Mac OS X上对此输入文件进行排序时(无论我使用的是Mac OS X Yosemite附带的GNU sort 5.93,还是使用Homebrew安装的GNU排序版本8.23),我得到了这个排序结果:

mac-os-x$ env -i LANG=en_US.utf-8 LC_ALL=en_US.utf-8 /usr/bin/sort unsorted.txt
'foo'
foo
?
?
?

mac-os-x$ echo `sw_vers -productName` `sw_vers -productVersion`
Mac OS X 10.10.1

mac-os-x$ /usr/bin/sort --version | head -1
sort (GNU coreutils) 5.93
Run Code Online (Sandbox Code Playgroud)

当我在Linux上对具有相同语言环境设置的相同文件进行排序时(我在Centos 5.5和CentOS 6.5上进行了测试),得到了不同的结果:

linux-centos-6.5$ env -i LANG=en_US.utf-8 LC_ALL=en_US.utf-8 /bin/sort unsorted.txt
?
?
foo
'foo'
?

linux-centos-6.5$ cat /etc/redhat-release
CentOS release 6.5 (Final)

linux-centos-6.5$ /bin/sort --version | head -1
sort (GNU coreutils) 8.4
Run Code Online (Sandbox Code Playgroud)

请注意日语假名与英语的不同位置,以及仅由单引号不同的两行之间的不同排序顺序.

为了添加另一个变体,我注意到在一个非常旧的FreeBSD 6盒子上,我得到了与OS X相同的排序顺序:

freebsd-6.0$ env -i LANG=en_US.utf-8 LC_ALL=en_US.utf-8 /usr/bin/sort unsorted.txt
'foo'
foo
?
?
?

freebsd-6.0$ uname -rs
FreeBSD 6.0-RELEASE

freebsd-6.0$ sort --version | head -1
sort (GNU coreutils) 5.3.0-20040812-FreeBSD
Run Code Online (Sandbox Code Playgroud)

我希望在每种情况下排序顺序都相同,因为所有情况都使用GNU排序,所有都具有相同的区域设置.我尝试LC_COLLATE单独显示设置,并尝试使用LC_COLLATE=C按字节顺序强制排序,但这并没有改变任何结果.

为什么我的示例输入文件在OS X和Linux中排序不同?我怎么能强制两个系统生成相同排序的文本(我不关心哪个变体,只要它们在两者之间是一致的)?

Paw*_*iel 2

看起来 -你的 Linuxsort没有保留正确的 UTF-8 顺序

\n

您的(第一个字母)的十六进制 UTF-8 表示形式unsorted.txt为:

\n
\n

\xe3\x82\xa6 - 30A6

\n

- 0066

\n

\xe3\x83\x81 - 30C1

\n

'foo' - 0027

\n

\xe6\xb4\xa5 - 6D25

\n

摘自http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=%E3%82%A6&mode=char

\n
\n

因此,根据 unicode 排序规则(http://www.unicode.org/Public/UCA/latest/allkeys.txt)进行正确排序将是:

\n
\n

\'foo\' - 第 487 行

\n

foo - 第 8966 行

\n

\xe3\x82\xa6 - 第 20875 行

\n

\xe3\x83\x81 - 第 21004 行

\n

\xe6\xb4\xa5 - 不在文件中

\n
\n

因此,为了回答你的问题,你的 Linux 机器提供了错误的排序表来sort运行。不幸的是,我无法说出可能的原因是什么。

\n

PS:这里有和你类似的问题。

\n

编辑

\n

正如 @ninjalj 注意到的,glibc 不使用 UCA,而是使用 ISO-14651。此错误报告建议迁移到 UCA。不幸的是,它仍然没有解决。

\n

另外,它可能与 MacOSX 上ls 不区分大小写的问题有关。有些人甚至认为这与 HFS 文件系统有关。

\n

  • 正确的排序顺序取决于区域设置,不存在“正确的 UTF-8 顺序”之类的东西。 (2认同)