Linux 的 sort 如何對字串進行排序

介紹

一切都始於一個旨在整合地址資訊的簡短腳本。 電子郵件 從使用者郵件清單中取得員工,從人力資源資料庫取得員工職位。兩個清單均匯出為 Unicode 文字檔案。 UTF-8的 並以 Unix 行尾儲存。

內容 郵件.txt

Иванов Андрей;ia@example.com

內容 buhg.txt

Иванова Алла;маляр
Ёлкина Элла;крановщица
Иванов Андрей;слесарь
Абаканов Михаил;маляр

使用 Unix 指令對檔案進行排序 分類 並輸入到 Unix 程式的輸入中 加入,意外地以錯誤結束:

$> sort buhg.txt > buhg.srt
$> sort mail.txt > mail.srt
$> join buhg.srt mail.srt > result
join: buhg.srt:4: is not sorted: Иванов Андрей;слесарь

目視檢查排序結果,排序大致正確,但在男女姓氏匹配的情況下,女性姓氏排在男性姓氏之前:

$> sort buhg.txt
Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванова Алла;маляр
Иванов Андрей;слесарь

看起來像是 Unicode 排序故障或排序演算法的女性主義扭曲。當然,第一種說法更為合理。

我們先把它推遲一下 加入 讓我們關注 分類。讓我們嘗試使用科學的反覆試驗方法來解決這個問題。首先,讓我們將語言環境從 EN_USru_RU。要進行排序,只需設定一個環境變數即可 LC_COLLATE,但我們不會在瑣事上浪費時間:

$> LANG=ru_RU.UTF-8 sort buhg.txt
Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванова Алла;маляр
Иванов Андрей;слесарь

一切都沒有改變。

讓我們嘗試將檔案重新編碼為單字節編碼:

$> iconv -f UTF-8 -t KOI8-R buhg.txt 
 | LANG=ru_RU.KOI8-R sort 
 | iconv -f KOI8-R -t UTF8

再次,什麼都沒有改變。

沒辦法,我只能上網找解決方法。雖然沒有直接提到俄羅斯姓氏,但提出了其他排序奇怪之處的問題。例如,這裡有這樣一個問題: unix 排序將「-」(破折號)字元視為不可見。簡而言之,字串“ab”,“aa”,“ac”排序為“aa”,“ab”,“ac”。

答案是無所不在的:使用程式設計師的語言環境 “C” 你就會幸福。讓我們試試看:

$> LANG=C sort buhg.txt
Ёлкина Элла;крановщица
Абаканов Михаил;маляр
Иванов Андрей;слесарь
Иванова Алла;адвокат

有些事情已經改變了。伊凡諾夫一家按照正確的順序排好隊,儘管約爾金娜不知從哪裡溜走了。讓我們回到最初的問題:

$> LANG=C sort buhg.txt > buhg.srt
$> LANG=C sort mail.txt > mail.srt
$> LANG=C join buhg.srt mail.srt > result

正如互聯網所承諾的那樣,它運行起來沒有錯誤。儘管 Yolkina 在第一行,但情況仍然如此。

問題似乎已經解決了,但為了以防萬一,我們嘗試另一種俄語編碼 - Windows CP1251:

$> iconv -f UTF-8 -t CP1251 buhg.txt 
 | LANG=ru_RU.CP1251 sort 
 | iconv -f CP1251 -t UTF8 

奇怪的是,排序結果將與語言環境相匹配 “C”,因此整個範例運行沒有錯誤。這是一種神秘主義。

我不喜歡程式設計中的神秘主義,因為它通常會掩蓋錯誤。我們必須認真研究它是如何運作的。 分類 它會產生什麼影響 LC_COLLATE .

最後,我將嘗試回答以下問題:

  • 為什麼女性姓氏排序錯誤
  • 為什麼 LANG=ru_RU.CP1251 結果等同於 郎=C
  • 你為什麼 分類 и 加入 關於排序行順序的不同想法
  • 為什麼我的所有例子都有錯誤
  • 最後,如何根據你的喜好對線路進行排序

Unicode 排序

第一站是技術報告#10,標題為 Unicode 排序演算法 在線 unicode.org。該報告包含大量技術細節,因此我將對其主要想法進行簡要概述。

整理 — 字串的「比較」是任何排序演算法的基礎。演算法本身可能不同(「冒泡」、「合併」、「快速」),但它們都將使用一對字串的比較來確定它們的順序。

對自然語言中的字串進行排序是一個相當困難的問題。即使在最簡單的單字節編碼中,與英語拉丁字母有任何不同的字母表中的字母順序將不再與這些字母編碼的數值順序一致。所以在德語字母表中 Ö 位於之間 О и P以及編碼 CP850 她介於 ÿ и Ü.

您可以嘗試從特定的編碼中抽象化出來,並考慮按特定順序排列的「理想」字母,就像在 Unicode 中一樣。編碼 UTF8編碼, UTF16編碼 或單字節 KOI8-R (如果需要 Unicode 的有限子集)將給出字母的不同數字表示,但引用基底表的相同元素。

事實證明,即使我們從頭開始建立一個符號表,我們也無法為其中的符號分配通用順序。在使用相同字母的不同國家字母表中,這些字母的順序可能不同。例如,法語 Æ 將被視為連字並按字串排序 AE。在挪威語中, Æ 將是位於 Z。順便說一下,除了像 Æ 有些字母可以用多種符號來書寫。所以在捷克字母表中有一個字母 Ch,位於 H и I.

除了字母表的差異外,還有其他國家傳統也會影響分類。具體來說,出現這樣的問題:由大寫和小寫字母組成的單字在字典中應該按照什麼順序出現?此外,排序可能會受到標點符號的具體使用的影響。在西班牙語中,倒置的問號(你聽音樂嗎?)。在這種情況下,顯然疑問句不應該被分組到字母表之外的單獨簇中,但是如何對帶有其他標點符號的行進行排序呢?

我不會詳細討論與歐洲語言有很大差異的語言中的字串排序。請注意,在從右到左或從上到下書寫的語言中,行中的字符很可能按閱讀順序存儲,甚至非字母腳本也有自己的按字符對行進行排序的方式。例如,象形文字可以按其風格排序(漢字鍵)或透過發音。說實話,我不知道表情符號應該如何組織,但也可以為它們想出一些東西。

基於上述特點,制定了基於Unicode表進行字串比較的基本要求:

  • 字串比較不依賴字元在代碼表中的位置;
  • 形成單一符號的符號序列被簡化為規範形式(A + 頂部圓圈與 Å);
  • 比較字串時,將在字串的上下文中考慮字符,並在必要時將其與相鄰字符組合成單個比較單元(Ch 捷克語)或分成幾隻(Æ 法語)
  • 必須配置所有國家特徵(字母表、大寫/小寫、標點符號、書寫系統順序),包括手動分配順序(表情符號);
  • 比較不僅對於排序很重要,而且在許多其他地方也很重要,例如指定字串範圍(在 打壞);
  • 比較應該相當快完成。

此外,報告的作者也制定了演算法開發人員不應該依賴的比較屬性:

  • 比較演算法不應該要求每種語言都有一組單獨的字元(俄語和烏克蘭語共享大部分西里爾字元);
  • 比較不應該基於 Unicode 表中字元的順序;
  • 字串權重不應該是字串的屬性,因為同一個字串在不同的文化背景下可能有不同的權重;
  • 合併或拆分時行權重可能會發生變化(從 x < y 但這並不意味著 xz < yz);
  • 從排序演算法的角度來看,具有相同權重的不同行被認為是相等的。可以為此類字串引入額外的排序,但可能會降低效能;
  • 重複排序時,具有相同權重的行可能會改變位置。魯棒性是特定排序演算法的屬性,而不是字串比較演算法的屬性(參見上一點);
  • 隨著文化傳統的完善/改變,分類規則可能會隨著時間而改變。

也規定比較演算法對正在處理的字串的語意一無所知。因此,僅由數字組成的字串不應作為數字進行比較,並且在英文名稱列表中不應刪除冠詞(披頭四樂團).

為了滿足所有指定的要求,提出了一種多層(實際上是四級)表排序演算法。

首先,將字串中的字元轉換為規範形式並分組到比較單元。每個比較單元被分配若干個權重,對應若干個比較等級。比較單元的權重是有序集合(在本例中為整數)的元素,可以比較為大於或小於。特殊意義 忽略 (0x0)表示在相應的比較層級上,該單元不參與比較。字串比較可以重複多次,使用對應層級的權重。在每個層級中,兩個字串的比較權重單元會依序相互比較。

在不同國家傳統的演算法實作中,係數的數值可能會有所不同,但 Unicode 標準包含一個基本的權重表 - “預設 Unicode 排序元素表” (杜塞特)。我想指出的是,設定變數 LC_COLLATE 其實就是字串比較函數中權重表的選擇的一個指示。

加權因素 杜塞特 安排如下:

  • 在第一級,所有字母都轉換為相同的大小寫,變音符號被丟棄,標點符號(不是全部)被忽略;
  • 在第二級,只考慮變音符號;
  • 在第三級,只考慮大小寫;
  • 在第四級,只考慮標點符號。

比較需要經過幾個階段:首先,比較第一級係數;如果權重匹配,則與第二級的權重進行第二次比較;然後可能是第三和第四個。

當行包含具有不同權重的匹配比較單元時,比較結束。在所有四個層級上具有相同權重的行被視為彼此相等。

這個演算法(以及一系列額外的技術細節)被命名為第 10 號報告 - “Unicode 排序演算法” (加州大學洛杉磯分校).

此時,我們範例中的排序行為變得更加清晰了。將其與 Unicode 標準進行比較會很好。

用於測試實現 加州大學洛杉磯分校 有一個特殊的 測試, 使用 比例文件,實施 杜塞特。您可以在比例文件中找到各種有趣的東西。例如,麻將和歐洲骨牌的順序,以及一副牌的花色順序(符號 1F000 以及進一步)。牌的花色依橋牌規則-PCBT排列,花色的牌依照T、2,3、XNUMX……K的順序排列。

手動檢查行排序的正確性 杜塞特 會相當繁瑣,但幸運的是,有一個用於處理 Unicode 的庫的模型實現 -”Unicode 的國際元件“(重症監護室).

在這個圖書館的網站上, IBM,有演示頁面,包括 字串比較演算法頁面。我們用預設設定輸入測試行,瞧,我們得到了完美的俄語排序。

Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванов Андрей;слесарь
Иванова Алла;адвокат

順便說一下,在網站上 重症監護室 您可以找到處理標點符號時比較演算法的操作的說明。在範例中 排序規則常見問題解答 撇號和連字號將被忽略。

Unicode 幫助了我們,但我們仍然需要尋找導致這種奇怪行為的原因 分類 в Linux 必須在別的地方。

glibc 中的排序

快速查看實用程式原始碼 分類GNU核心實用程式 表明在實用程式本身中,本地化歸結為列印變數的當前值 LC_COLLATE 在調試模式下運行時:

$ sort --debug buhg.txt > buhg.srt
sort: using ‘en_US.UTF8’ sorting rules

字串比較由標準函數執行 斯特科爾,這意味著所有有趣的東西都在圖書館裡 glibc的.

維基 該項目 glibc的 專用於字串比較 一個段落。從這段話可以理解, glibc的 排序基於我們已經知道的演算法 加州大學洛杉磯分校 (Unicode 排序演算法)和/或接近它的標準 ISO 14651 (國際字串排序和比較)。關於最後一個標準,需要注意的是,在網站上 standards.iso.org ISO 14651 官方宣佈公開發布,但相應的連結指向一個不存在的頁面。谷歌提供了幾頁官方網站的鏈接,這些網站提供以一百歐元的價格購買該標準的電子版,但在搜索結果的第三頁或第四頁,你還會找到直接鏈接 PDF。總體而言,該標準實際上與 加州大學洛杉磯分校,但讀起來比較枯燥,因為它沒有包含字串排序的國家特色的生動例子。

最有趣的訊息 維基 有一個連結 錯誤追蹤器 討論字串比較的實現 glibc的。從討論中可以看出, glibc的 用於比較字串 的ISO許納亞表 通用模板表 (CTT),地址見附錄 A 標準 ISO 14651。 2000 年至 2015 年期間,此表 glibc的 沒有維護者,並且與當前版本的標準有很大不同(至少在外部)。從 2015 年到 2018 年,新版表格進行了改編,目前您有機會在現實生活中同時遇到新版表格(CentOS 8的),以及舊的 (CentOS 7的).

現在我們已經掌握了有關演算法和輔助表的所有信息,我們可以回到最初的問題並了解如何在俄語語言環境中正確對字串進行排序。

ISO 14651 / 14652

我們感興趣的表的源代碼 CTT 在大多數發行版中 Linux 在目錄中 /usr/share/i18n/locales/。表格本身位於文件中 iso14651_t1_common。那麼這個檔案就是一個指令 複製 iso14651_t1_common 包含在文件中 iso14651_t1,而這些又被納入國家檔案,包括 EN_US и ru_RU。在大多數發行版中 Linux 所有來源檔案都包含在基本安裝中,但如果沒有,您將必須從發行版中安裝附加套件。

文件結構 iso14651_t1 它可能看起來非常冗長,並且構造名稱的規則並不明顯,但如果你弄清楚了,一切都會變得非常簡單。該結構在標準中描述 ISO 14652,可從網站下載副本 開放標準組織。關於文件格式的另一種描述可以在 規格 POSIX開放集團。除了閱讀標準之外,您還可以研究函數的原始程式碼 整理閱讀 в glibc/locale/程式/ld-collat​​e.c.

文件結構如下:

預設情況下,此符號作為轉義符,#符號後的行尾為註解。這兩個符號都可以重新定義,這就是新版本表中所做的:

escape_char /
comment_char %

該文件將包含以下格式的標記 (在哪裡 x — 十六進位數字)。這是編碼中 Unicode 代碼點的十六進位表示 UCS-4 (UTF-32的)。尖括號中的所有其他元素(包括 , 等等)被視為簡單的字串常數,在上下文之外沒有特殊意義。

弦樂 LC_COLLATE 告訴我們描述字串比較的資料接下來開始。

首先,指定比較表中的權重名稱和符號組合名稱。一般來說,這兩類名稱屬於兩個不同的實體,但在實際文件中它們卻混雜在一起。尺度的名稱由關鍵字指定 排序符號 (比較字元)因為當比較具有相同權重的 Unicode 字元時,它們將被視為等效字元。

該文件目前修訂版中該部分的總長度約為 900 行。我從幾個地方舉了一些例子來展示名稱的任意性和幾種文法。

LC_COLLATE

collating-symbol <RES-1>
collating-symbol <BLK>
collating-symbol <MIN>
collating-symbol <WIDE>
...
collating-symbol <ARABIC>
collating-symbol <ETHPC>
collating-symbol <OSMANYA>
...
collating-symbol <S1D000>..<S1D35F>
collating-symbol <SFFFF> % Guaranteed largest symbol value. Keep at end of this list
...
collating-element <U0413_0301> from "<U0413><U0301>"
collating-element <U0413_0341> from "<U0413><U0341>"

  • 排序符號 註冊一個字串 奧斯曼尼亞 在比例名稱表中
  • 排序符號.. 註冊由前綴組成的名稱序列 S 以及來自的十六進制數字後綴 1D0001天35樓.
  • FFFF в 排序符號 看起來像是十六進位表示法中的一個大無符號整數,但是 這只是一個看起來像的名字
  • 產品名稱 表示編碼中的代碼點 UCS-4
  • 排序元素從 ” “ 為 Unicode 點對註冊一個新名稱。

一旦定義了權重名稱,就會指定實際權重。由於只有大於或小於關係在比較中才重要,因此權重由清單名稱的簡單序列決定。首先列出「較輕」的重量,然後列出「較重」的重量。讓我提醒你,每個 Unicode 字元都被分配了四種不同的權重。在這裡,它們被組合成一個有序的序列。理論上,任何符號名稱都可以在四個層級中的任何一個層級上使用,但註釋表明開發人員在心理上按層級區分名稱。

% Symbolic weight assignments

% Third-level weight assignments
<RES-1>
<BLK>
<MIN>
<WIDE>
...
% Second-level weight assignments
<BASE>
<LOWLINE> % COMBINING LOW LINE
<PSILI> % COMBINING COMMA ABOVE
<DASIA> % COMBINING REVERSED COMMA ABOVE
...
% First-level weight assignments
<S0009> % HORIZONTAL TABULATION 
<S000A> % LINE FEED
<S000B> % VERTICAL TABULATION
...
<S0434> % CYRILLIC SMALL LETTER DE
<S0501> % CYRILLIC SMALL LETTER KOMI DE
<S0452> % CYRILLIC SMALL LETTER DJE
<S0503> % CYRILLIC SMALL LETTER KOMI DJE
<S0453> % CYRILLIC SMALL LETTER GJE
<S0499> % CYRILLIC SMALL LETTER ZE WITH DESCENDER
<S0435> % CYRILLIC SMALL LETTER IE
<S04D7> % CYRILLIC SMALL LETTER IE WITH BREVE
<S0454> % CYRILLIC SMALL LETTER UKRAINIAN IE
<S0436> % CYRILLIC SMALL LETTER ZHE

最後,是權重表本身。

權重部分包含在關鍵字行中 order_start и order_end。附加選項 order_start 確定在每個比較層級上搜尋行的方向。預設是 前鋒。節主體由包含字元代碼及其四種權重的行組成。字元代碼可以由字元本身、代碼點或先前定義的符號名稱表示。權重也可以透過符號名稱、代碼點或符號本身來指定。如果使用代碼點或符號,則它們的權重與代碼點的數值(Unicode 表中的位置)相同。未明確指定的符號(據我理解)被認為以與 Unicode 表中的位置相符的主要權重分配給表。特殊權重值 忽略 意味著在相應的比較層級忽略給定的符號。

為了展示尺度的結構,我選擇了三個相當明顯的片段:

  • 完全忽略的字符
  • 前兩層相當於數字三的符號
  • 西里爾字母的開頭,不包含變音符號,因此主要按第一級和第三級排序。

order_start forward;forward;forward;forward,position
<U0000> IGNORE;IGNORE;IGNORE;IGNORE % NULL (in 6429)
<U0001> IGNORE;IGNORE;IGNORE;IGNORE % START OF HEADING (in 6429)
<U0002> IGNORE;IGNORE;IGNORE;IGNORE % START OF TEXT (in 6429)
...
<U0033> <S0033>;<BASE>;<MIN>;<U0033> % DIGIT THREE
<UFF13> <S0033>;<BASE>;<WIDE>;<UFF13> % FULLWIDTH DIGIT THREE
<U2476> <S0033>;<BASE>;<COMPAT>;<U2476> % PARENTHESIZED DIGIT THREE
<U248A> <S0033>;<BASE>;<COMPAT>;<U248A> % DIGIT THREE FULL STOP
<U1D7D1> <S0033>;<BASE>;<FONT>;<U1D7D1> % MATHEMATICAL BOLD DIGIT THREE
...
<U0430> <S0430>;<BASE>;<MIN>;<U0430> % CYRILLIC SMALL LETTER A
<U0410> <S0430>;<BASE>;<CAP>;<U0410> % CYRILLIC CAPITAL LETTER A
<U04D1> <S04D1>;<BASE>;<MIN>;<U04D1> % CYRILLIC SMALL LETTER A WITH BREVE
<U0430_0306> <S04D1>;<BASE>;<MIN>;<U04D1> % CYRILLIC SMALL LETTER A WITH BREVE
...
<U0431> <S0431>;<BASE>;<MIN>;<U0431> % CYRILLIC SMALL LETTER BE
<U0411> <S0431>;<BASE>;<CAP>;<U0411> % CYRILLIC CAPITAL LETTER BE
<U0432> <S0432>;<BASE>;<MIN>;<U0432> % CYRILLIC SMALL LETTER VE
<U0412> <S0432>;<BASE>;<CAP>;<U0412> % CYRILLIC CAPITAL LETTER VE
...
order_end

現在我們可以回到文章開頭對範例進行排序。埋伏就在權重表的這個部分:

<U0020> IGNORE;IGNORE;IGNORE;<U0020> % SPACE
<U0021> IGNORE;IGNORE;IGNORE;<U0021> % EXCLAMATION MARK
<U0022> IGNORE;IGNORE;IGNORE;<U0022> % QUOTATION MARK
...

很明顯,這個表中的標點符號來自表格 ASCII碼 (包括空格)在比較字串時幾乎總是被忽略。唯一的例外是除了出現在匹配位置的標點符號之外的所有內容都匹配的行。我的範例(排序後)中比較演算法的行如下所示:

АбакановМихаилмаляр
ЁлкинаЭллакрановщица
ИвановаАлламаляр
ИвановАндрейслесарь

考慮到在權重表中,俄語的大寫字母位於小寫字母之後(在第三級 比…重 ),排序看起來完全正確。

設定變數時 LC_COLLATE=C 載入一個特殊的表,指定逐字節比較

static const uint32_t collseqwc[] =
{
  8, 1, 8, 0x0, 0xff,
  /* 1st-level table */
  6 * sizeof (uint32_t),
  /* 2nd-level table */
  7 * sizeof (uint32_t),
  /* 3rd-level table */
  L'x00', L'x01', L'x02', L'x03', L'x04', L'x05', L'x06', L'x07',
  L'x08', L'x09', L'x0a', L'x0b', L'x0c', L'x0d', L'x0e', L'x0f',

...
  L'xf8', L'xf9', L'xfa', L'xfb', L'xfc', L'xfd', L'xfe', L'xff'
};

由於在 Unicode 中代碼點 Ё 位於 A 之前,因此字串會依照對應順序排序。

文字和二進位表

顯然,字串比較是一種極為常見的操作,而表解析 CTT 這是一個相當昂貴的過程。為了優化對錶的訪問,使用命令將其編譯為二進位形式 localedef.

團隊 localedef 以包含國家特徵表的文件作為參數(選項 -i),其中所有字元都由 Unicode 點表示,以及 Unicode 點到特定編碼字元的對應檔案(選項 -f)。此工作的結果是建立具有最後一個參數中指定的名稱的區域設定的二進位檔案。

格里布 支援兩種二進位檔案格式:“傳統”和“現代”。

傳統格式假設語言環境名稱是 /usr/lib/locale/。此子目錄儲存二進位。 LC_COLLATE, LC_CTYPE, LC_TIME 等文件 LC_IDENTIFICATION 包含語言環境的正式名稱(可能與目錄名稱不同)和註釋。

現代格式涉及將所有語言環境儲存在一個檔案中 /usr/lib/locale/locale-archive,它被映射到所有進程的虛擬記憶體中,使用 glibc的。現代格式的語言環境名稱受到一些規範化 - 編碼名稱中僅保留小寫的數字和字母。所以 ru_RU.KOI8-R,將保存為 ru_RU.koi8r.

在目前目錄以及目錄中搜尋輸入文件 /usr/share/i18n/locales/ и /usr/share/i18n/charmaps/ 對於文件 CTT 和編碼文件。

例如,該團隊

localedef -i ru_RU -f MAC-CYRILLIC ru_RU.MAC-CYRILLIC

編譯文件 /usr/share/i18n/locales/ru_RU 使用編碼文件 /usr/share/i18n/charmaps/MAC-CYRILLIC.gz 並將結果保存在 /usr/lib/locale/locale-archive 以...之名 ru_RU.maccyrillic

如果你設定一個變數 LANG =的en_US.UTF-8 то glibc的 將按照以下檔案和目錄序列搜尋區域設定二進位檔案:

/usr/lib/locale/locale-archive
/usr/lib/locale/en_US.UTF-8/
/usr/lib/locale/en_US/
/usr/lib/locale/enUTF-8/
/usr/lib/locale/en/

如果某個區域同時具有傳統格式和現代格式,則優先考慮現代格式。

您可以使用以下命令查看已編譯的語言環境列表 locale -a.

準備自己的比較表

現在,有了這些知識,您就可以建立自己的完美字串比較表。表格應正確比較俄語字母,包括字母Ё,同時根據表格考慮標點符號 ASCII碼.

準備自己的排序表的過程包括兩個階段:編輯權重表並使用命令將其編譯為二進位形式 localedef.

為了以最少的編輯工作量調整比較表,格式如下 ISO 14652 提供了用於調整現有表的權重的部分。該部分以關鍵字開頭 重新排序 並指示執行替換之後的位置。該部分以一行結尾 重新排序結束。如果需要更正表格的幾個部分,則為每個部分建立一個部分。

我複製了文件的新版本 iso14651_t1_common и ru_RU 從儲存庫 glibc的 到您的主目錄 ~/.local/share/i18n/locales/ 並稍微編輯該部分 LC_COLLATE в ru_RU。新版本的文件與我的版本完全相容 glibc的。如果您想使用舊版本的文件,您將必須更改符號名稱和表中替換的開始位置。

LC_COLLATE
% Copy the template from ISO/IEC 14651
copy "iso14651_t1"
reorder-after <U000D>
<U0020> <S0020>;<BASE>;<MIN>;<U0020> % SPACE
<U0021> <S0021>;<BASE>;<MIN>;<U0021> % EXCLAMATION MARK
<U0022> <S0022>;<BASE>;<MIN>;<U0022> % QUOTATION MARK
...
<U007D> <S007D>;<BASE>;<MIN>;<U007D> % RIGHT CURLY BRACKET
<U007E> <S007E>;<BASE>;<MIN>;<U007E> % TILDE
reorder-end
END LC_COLLATE

事實上,有必要改變 LC_IDENTIFICATION 這樣它們就指向了語言環境 ru_MY,但在我的例子中,這不是必需的,因為我從語言環境搜尋中排除了檔案 區域設定存檔.

localedef 透過變數處理資料夾中的文件 I18NPATH 您可以新增額外的目錄來搜尋輸入文件,並且可以將儲存二進位檔案的目錄指定為帶有斜線的路徑:

$> I18NPATH=~/.local/share/i18n localedef -i ru_RU -f UTF-8 ~/.local/lib/locale/ru_MY.UTF-8

POSIX 表明 您可以將絕對路徑寫入包含語言環境檔案的目錄,以正斜線開頭,但 glibc的 в Linux 所有路徑都是從基底目錄計算的,可以透過變數覆蓋 位置路徑。安裝後 LOCPATH=~/.local/lib/locale/ 所有與本地化相關的文件將僅在我的資料夾中搜尋。設定變數時的語言環境存檔 位置路徑 被忽略。

這是決定性的測試:

$> LANG=ru_MY.UTF-8 LOCPATH=~/.local/lib/locale/ sort buhg.txt
Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванов Андрей;слесарь
Иванова Алла;адвокат

萬歲!我們做到了!

有些錯誤

我已經回答了一開始提出的有關字串排序的問題,但我仍然有幾個關於可見和不可見錯誤的問題。

讓我們回到最初的問題。

還有節目 分類 和程序 加入 使用相同的字串比較函數 glibc的。怎麼會這樣呢 加入 按命令排序的行出現排序錯誤 分類 在語言環境中 面對en_US.UTF-8?答案很簡單: 分類 比較整個字串,並且 加入 僅比較鍵,預設情況下鍵是從字串的開頭到第一個空白字元。在我的範例中,這導致了一條錯誤訊息,因為行中第一個單字的排序與整行的排序不符。

區域設置 “C” 確保在排序的字串中,直到第一個空格的初始子字串也將被排序,但這只能掩蓋錯誤。可以選擇資料(姓氏相同但名字不同的人),這些資料會產生不正確的文件合併結果,而不會出現錯誤訊息。如果我們想 加入 如果您按全名合併文件行,正確的方法是明確指定欄位分隔符號並按關鍵欄位排序,而不是按整行排序。在這種情況下,合併將正確進行,並且在任何語言環境中都不會出現錯誤:

$> sort -t ; -k 1 buhg.txt > buhg.srt
$> sort -t ; -k 1 mail.txt > mail.srt
$> join -t ; buhg.srt mail.srt > result

成功執行編碼範例 CP1251 包含另一個錯誤。問題是,在我所知道的所有發行版中 Linux 軟體包中沒有已編譯的語言環境 ru_RU.CP1251。如果未找到已編譯的語言環境,則 分類 默默地使用逐字節比較,這正是我們所觀察到的。

順便說一句,還有另一個與編譯語言環境不可用有關的小故障。團隊 LOCPATH=/tmp 區域設定-a 將列出所有語言環境 區域設定存檔,但變數集 位置路徑 對於所有程序(包括程序本身) 當地)這些語言環境將無法使用。

$> LOCPATH=/tmp locale -a | grep en_US
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_COLLATE to default locale: No such file or directory
en_US
en_US.iso88591
en_US.iso885915
en_US.utf8

$> LC_COLLATE=en_US.UTF-8 sort --debug
sort: using ‘en_US.UTF-8’ sorting rules

$> LOCPATH=/tmp LC_COLLATE=en_US.UTF-8 sort --debug
sort: using simple byte comparison

結論

如果你是一名程式設計師,習慣將字串視為一組字節,那麼你的選擇是 LC_COLLATE=C.

如果您是語言學家或字典編譯者,您可能想要編譯自己的語言環境。

如果你是一個普通用戶,那麼你只需要習慣這個指令 ls -a 產生以點開頭的文件和以字母開頭的文件混合的文件,並且 午夜指揮官它使用其內部函數對名稱進行排序,將以點開頭的文件放在列表頂部。

引用

報告編號10 Unicode 排序演算法

unicode.org 上的字元權重

重症監護室 — IBM Unicode 工作庫的實作。

測試排序 重症監護室

符號權重 ISO 14651

帶有比例的文件格式描述 ISO 14652

中的字串比較討論 glibc的

來源: www.habr.com

添加評論