在 unicode 文本上使用 uniq
我想從帶有敘利亞語腳本單詞的文件中刪除重複的行。源文件有 3 行,第 1 和第 3 行是相同的。
$ cat file.txt ܐܒܘܢ ܢܗܘܐ ܐܒܘܢ
當我使用
sort
anduniq
時,結果假定所有 3 行都是相同的,這是錯誤的:$ cat file.txt | sort | uniq -c 3 ܐܒܘܢ
將語言環境顯式設置為敘利亞語也無濟於事。
$ LC_COLLATE=syr_SY.utf8 cat file.txt | sort | uniq -c 3 ܐܒܘܢ
為什麼會這樣?如果這很重要,我正在使用 Kubuntu 18 和 bash。
uniq
在 Ubuntu 上找到的 GNU 實現-c
不報告連續相同行的計數,而是報告排序相同的連續行的計數¹。GNU 系統上的大多數國際語言環境都有這樣的錯誤,即許多完全不相關的字元已使用相同的排序順序定義,其中大多數是因為根本沒有定義它們的排序順序。大多數其他作業系統確保所有字元都有不同的排序順序。
$ expr ܐ = ܒ 1
(
expr
的=
運算符,對於非數字參數,如果操作數排序相同,則返回 1,否則返回 0)。這與
ar_SY.UTF-8
or相同en_GB.UTF-8
。您需要的是一個區域設置,這些字元已被賦予不同的排序順序。如果 Ubuntu 有敘利亞語的語言環境,您可以期望這些字元被賦予不同的排序順序,但 Ubuntu 沒有這樣的語言環境。
您可以查看
locale -a
支持的語言環境列表的輸出。您可以通過執行dpkg-reconfigure locales
as來啟用更多語言環境root
。您還可以localedef
根據 中的定義文件手動定義更多語言環境/usr/share/i18n/locales
,但您將在那裡找不到敘利亞語的數據。請注意,在:
LC_COLLATE=syr_SY.utf8 cat file.txt | sort | uniq -c
您只是為
cat
命令設置 LC_COLLATE 變數(這不會影響它輸出文件內容的方式,cat
不關心排序規則,甚至不關心字元編碼,因為它不是文本實用程序)。您想同時為sort
和設置它uniq
。您還希望設置LC_CTYPE
為具有 UTF-8 字元集的語言環境。由於您的系統沒有
syr_SY.utf8
語言環境,這與使用C
語言環境(預設語言環境)相同。實際上,這裡的 C 語言環境或 C.UTF-8 可能是您想要使用的語言環境。
在這些語言環境中,排序順序基於程式碼點、C.UTF-8 的 Unicode 程式碼點、C 的字節值,但最終與 UTF-8 字元編碼具有該屬性相同。
$ LC_ALL=C expr ܐ = ܒ 0 $ LC_ALL=C.UTF-8 expr ܐ = ܒ 0
所以:
(export LANG=ar_SY.UTF-8 LC_COLLATE=C.UTF-8 LANGUAGE=syr:ar:en unset LC_ALL sort <file | uniq -c)
您將擁有一個以 UTF-8 作為字元集的 LC_CTYPE、基於程式碼點的排序規則以及與您所在地區相關的其他設置,例如,如果 GNU coreutils
sort
或uniq
消息已被翻譯成敘利亞語或阿拉伯語的錯誤消息語言(他們還沒有)。如果您不關心其他設置,那麼使用起來同樣簡單(而且更便攜):
<file LC_ALL=C sort | LC_ALL=C uniq -c
或者
(export LC_ALL=C; <file sort | uniq -c)
正如@isaac 已經展示的那樣。
¹ 請注意,符合 POSIX
uniq
的實現並不意味著使用區域設置的排序算法比較字元串,而是進行字節到字節的相等比較。這在 2018 年版標準中得到了進一步澄清(請參閱相應的 Austin 組錯誤)。但 GNUuniq
目前確實使用strcoll()
,甚至在POSIXLY_CORRECT
; 它還有一個-i
不區分大小寫比較的選項,具有諷刺意味的是,它不使用語言環境資訊,只能在 ASCII 輸入上正常工作