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-8
,C.utf8
或C.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
(实际上,您会发现,如果要比较的两个字符串具有相同的字节数,GNUsort
会memcmp()
在调用之前先调用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 编码文本进行排序,使用C
或C.UTF-8
不会在功能上产生任何不同,但C
根据sort
实现的不同,使用可能会更有效。
现在,并非所有字节序列都会形成有效的 UTF-8,因此当涉及非 UTF-8 输入(即包含无法解码为 UTF-8 的字节序列的输入)时,您可能会发现行为有所不同之间。C
C.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_MAX
bytes 的行都是有效文本。在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