Sort

排序 LC_ALL=C 與 LC_ALL=C.utf8

  • January 29, 2020

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.utf8C.UTF-8(後者在我的經驗中更常見)不是 POSIX 標準化的語言環境,但無論在哪裡找到它們,它們都意味著具有 C 語言環境的大部分屬性,除了字元集是 UTF- 8.

LC_ALL=C.UTF-8 sort將根據程式碼點對輸入進行排序,但最終可能會在比較之前解碼 UTF-8,或者呼叫strcoll()/strxfrm()重型機器,考慮到 UTF-8,使用它就memcmp()足夠了。

在許多使用 Linux 作為核心的非嵌入式作業系統上發現了GNUsort和 GNU libc (這裡還在輸入中添加了 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 編碼的文本進行排序,使用CorC.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,sorton 的行為是未指定的。

附帶說明:在 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

引用自:https://unix.stackexchange.com/questions/564303