排序 LC_ALL=C 与 LC_ALL=C.utf8

排序 LC_ALL=C 与 LC_ALL=C.utf8

linux sort 命令区分 C 和 C.utf-8 语言环境吗?

排序手册说使用 LC_ALL=C 按字节值排序,但我看到 C.utf-8 也允许 utf8 值(而不仅仅是 ASCII) - 但排序手册根本没有引用此区域设置选项。

运行 LC_ALL=C sort file.txt 和 LC_ALL=C.utf8 sort file.txt 时,我没有看到两者之间有任何区别,无论文件是否具有 utf 8 字符,两者似乎都有效。

那么有什么已知的区别吗?

答案1

LC_ALL=C sort按字节值排序。它将按字节值对以任何字符集写入的任何输入进行排序,而不仅仅是 ASCII1。

UTF-8 编码具有按字节值排序与按 Unicode 代码点排序相同的属性(memcmp()会发现 U+1234 的编码大于 U+1233 或任何小于 0x1234 的 Unicode 代码点)。

C.utf-8C.utf8C.UTF-8(后者在我的经验中更常见)不是 POSIX 标准化的语言环境,但无论在哪里找到它们,它们都意味着是具有 C 语言环境的大部分属性的语言环境,除了字符集是 UTF- 8.

LC_ALL=C.UTF-8 sort会根据代码点对输入进行排序,但最终可能会在比较之前解码 UTF-8,或者调用strcoll()/strxfrm()重型机器,这最终会浪费精力,因为对于 UTF-8,使用memcmp()就足够了。

使用 GNUsort和许多使用 Linux 作为内核的非嵌入式操作系统上的 GNU libc (这里还在输入中添加了 GNUsort支持的 NUL 字符,尽管GNUstrcoll()不支持):

$ printf 'a\0£1\na\0€2\n' | LC_ALL=C ltrace -e strcoll -e memcmp sort
sort->memcmp("a\0\302\2431", "a\0\342\202\254", 5)                      = -1
a£1
a€2
$ printf 'a\0£1\na\0€2\n' | LC_ALL=C.UTF-8 ltrace -e strcoll -e memcmp sort
sort->strcoll("a", "a")                                                 = 0
sort->strcoll("\302\2431", "\342\202\2542")                             = -31
a£1
a€2

(实际上,您会发现,如果要比较的两个字符串具有相同的字节数,GNUsortmemcmp()在调用之前先调用strcoll(),以防它们相同,因为memcmp()与 相比,这样便宜strcoll())。

该输出的某些计时重复了 1,000,000 次:

$ printf 'a\0£1\na\0€2\n%.0s' {1..1000000} > file.test
$ wc -mc file.test
10000000 13000000 file.test
$ time LC_ALL=C sort file.test > /dev/null
LC_ALL=C sort file.test > /dev/null  0.74s user 0.06s system 390% cpu 0.205 total
$ time LC_ALL=C.UTF-8 sort file.test > /dev/null
LC_ALL=C.UTF-8 sort file.test > /dev/null  6.04s user 0.12s system 522% cpu 1.179 total

因此,要按代码点对 UTF-8 编码文本进行排序,使用CC.UTF-8不会在功能上产生任何不同,但C根据sort实现的不同,使用可能会更有效。

现在,并非所有字节序列都会形成有效的 UTF-8,因此当涉及非 UTF-8 输入(即包含无法解码为 UTF-8 的字节序列的输入)时,您可能会发现行为有所不同之间。CC.UTF-8仍然在 GNU 系统上:

$ print -l 'a\200b' 'a\201b' | LC_ALL=C sort -u
a�b
a�b
$ print -l 'a\200b' 'a\201b' | LC_ALL=C.UTF-8 sort -u
a�b

(其中 � 是我的终端模拟器对未知事物的再现)

在 C.UTF-8 中,strcoll()对不形成有效 UTF-8 文本的两个字符串返回 0,实际上报告它们具有相同的排序顺序。

在 C 语言环境中,任何由非 0 字节序列组成且长度不超过LINE_MAXbytes 的行都是有效文本。在C.UTF-8中,还有进一步的限制。这a\200b在 UTF-8 中无效,因此它不是文本,因此根据 POSIX,sort其行为未指定。

附带说明:在 GNU 系统上,虽然LC_ALL=C优先$LANGUAGE于消息语言,但LC_ALL=C.UTF-8并不优先。

$ LC_ALL=C LANGUAGE=fr:es:en sort /
sort: read failed: /: Is a directory
$ LC_ALL=C.UTF-8 LANGUAGE=fr:es:en sort /
sort: échec de lecture: /: est un dossier

还请注意,C区域设置字符集不必基于 ASCII,并且 ASCII 仅涵盖值 0 到 127。C使用 ASCII 的区域设置仍将字节 128 到 255 视为字符,尽管是未定义的字符。不过,区域C设置字符集必须保证每个字符一个字节,因此C区域设置字符集不能是 UTF-8

相关内容