排序 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 個字元,我都看不到兩者之間有任何區別,兩者似乎都有效。
那麼有什麼已知的區別嗎?
LC_ALL=C sort
按字節值排序。它將按字節值對以任何字元集寫入的任何輸入進行排序,而不僅僅是 ASCII¹。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()
足夠了。在許多使用 Linux 作為核心的非嵌入式作業系統上發現了GNU
sort
和 GNUlibc
(這裡還在輸入中添加了 GNUsort
支持的 NUL 字元,即使strcoll()
不支持):$ 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
(實際上,您會發現如果要比較的兩個字元串具有相同的字節數,GNU 會在
sort
呼叫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
orC.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
字節的行都是有效文本。在 C.UTF-8 中,還有更多限制。這a\200b
在 UTF-8 中無效,因此它不是文本,因此根據 POSIX,sort
on 的行為是未指定的。附帶說明:在 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