Tcl 程式設計/國際化
“一切皆為字串”,這是 Tcl 的座右銘。字串是一個(有限長度的)字元序列。那麼,什麼是字元?字元不同於字形,字形是我們在螢幕或紙張上看到的書寫元素,它代表字元,但同一個字形可以代表不同的字元,或者同一個字元可以用不同的字形來表示(例如,想想字型選擇器)。
此外,字元與記憶體中的位元組或位元組序列也不同。在離開 ASCII 的安全港後,這些位元組序列可能代表一個字元,但不是絕對的。
讓我們嘗試以下工作定義:“字元是書寫單元的抽象概念”。這通常等同於字母、數字或標點符號,但字元可以比這更多或更少。更多:連字,兩個或多個字母的組合,有時可以被視為一個字元(甚至排列在多行中,如日語 U+337F ㍿ 或阿拉伯語 U+FDFA ﷺ)。更少:新增到字元上的小標記(變音符號),例如 Nürnberg (U+00FC) 中的 ü 上的兩個點,可以將它變成一個新的“預組合”字元,如德語中那樣;或者它們可以被視為一個單獨的“組合字元”(例如 U+0308),在渲染時新增到前一個字形 (u, U+0075) 上,不移動渲染位置——這對於西班牙語、荷蘭語甚至(較舊的)英語正字法中這兩種點的功能(“變音符”)更為合理的處理方式:考慮 1950 年之前使用的拼寫“coöperation”。這種組合是打字機上“死鍵”的軟體等效項。
雖然是一個抽象的概念,但字元當然可以有屬性,最重要的是一個名稱:一個字串,它描述了它的功能、用法、發音等。各種名稱集已在 Postscript (/oumlaut) 或 HTML (ö) 中正式化。在技術應用中非常重要,當然是對字元分配一個數字(通常是一個非負整數)來識別它——這就是編碼的本質,其中這些數字更正式地稱為程式碼點。其他屬性可能是謂詞,例如“是否大寫”、“是否小寫”、“是否數字”。
這三個域之間的關係並不太複雜:編碼控制如何將 1..n 個位元組序列解釋為字元,反之亦然;渲染過程將抽象字元轉換為字形(通常透過畫素或向量圖案)。反之,從一組畫素中理解字元序列,並正確地表示它們,是更為困難的 OCR 技術,這裡不再贅述。
將字元對映到數字(程式碼點)的編碼工作,比電子計算的歷史更悠久。據說弗朗西斯·培根(1561-1626)在大約 1580 年使用了一種 5 位編碼,其中位用“a”或“b”表示,用於英文字母表(沒有字母 j 和 u!),遠早於萊布尼茨在 1679 年討論二進位制算術。在實際使用中,早期的一種編碼是 1932 年標準化的 5 位 Baudot/CCIT-2 電傳(穿孔紙帶)程式碼,它可以透過兩種模式來表示數字和一些標點符號。我曾在使用六位“Fieldata”字元的 Univac 機器上工作過,因為硬體字長為 36 位。雖然 IBM 在 EBCDIC 程式碼中使用了 8 位,但更著名的美國資訊交換標準程式碼 (ASCII) 在每個字元中基本上完成了相同的任務,每個字元使用 7 位,這足以用於大小寫基本拉丁語(英語)以及數字和一些標點符號以及其他“特殊”字元——由於硬體傾向於以 8 位位元組作為最小的記憶體單元,留下一位用於奇偶校驗或其他目的。
在美國以外,最重要的用途當然是容納更多代表國家書寫系統的字母——希臘語、俄語,或者幾乎每個歐洲國家都使用的帶重音或“變音符”的混合字元集。甚至英國也需要一個程式碼點來表示英鎊符號。一般的解決方案是使用在將 ASCII 實現為 8 位位元組時可用的 128 個附加位置,十六進位制 80..FF。許多這樣的編碼被定義和使用
- ISO 標準編碼 iso8859-.. (1-15)
- MS/DOS 內碼表 cp...
- Macintosh 內碼表 mac...
- Windows 內碼表 cp1...
東亞國家中國、日本和韓國都使用數以千計的字元集,因此“高 ASCII”方法在那裡不可行。相反,ASCII 概念擴充套件到 2x7 位模式,其中 94 個可列印的 ASCII 字元表示 94x94 矩陣中的行和列。這樣,所有字元程式碼在實踐中都是兩個位元組寬的,可以容納數千個漢字/日文漢字/韓文,以及數百個其他字元(ASCII、希臘語、俄語字母表、許多圖形字元)。這些國家的多位元組編碼是
- JIS C-6226(日本,1978 年;1983 年、1990 年大幅修訂)
- GB 2312-80(中國大陸,1980 年)
- KS C-5601(韓國,1987 年)
如果直接實現 2x7 模式,則此類編碼中的檔案無法與 ASCII 檔案區分開來,除非無法讀取。但是,它在日語電子郵件中確實有一些應用(在 8 位乾淨郵件伺服器出現之前形成的主流約定中),使用 ESC 控制字元的 ANSI 轉義程式碼被用於宣告文字部分為 ASCII 或 JIS C-6226,這被稱為“JIS 編碼”(或更準確地說是 ISO-2022-JP)。
在其他地方,為了以更實用的方式透明地處理這兩種型別的字串,擴充套件了“高 ASCII”方法,以便 00..7F 中的位元組以 ASCII 面值進行處理,而高位設定為 1(80..FF)的位元組被解釋為多位元組程式碼的半個位元組。例如,GB2312 中的第一個漢字,第 16 行第 1 列(簡稱為十進位制 1601),給出兩個位元組
16 + 32 + 128 = 176 = 0xB0 1 + 32 + 128 = 161 = 0xA1
這種實現被稱為“擴充套件 UNIX 程式碼”(EUC),分別在國家風格的 euc-cn(中國)、-jp(日本)、-kr(韓國)中使用。
為了增加“漢字湯”的混亂,與 euc-cn 和 euc-kr 不同,euc-jp(日本)並沒有在 Windows 或 Macintosh 平臺上廣泛採用,這些平臺傾向於使用不相容的 ShiftJIS,它重新排列程式碼以騰出空間用於舊的單位元組程式碼,用於音譯片假名字元。此外,臺灣和香港使用自己的“Big 5”編碼,它不使用 94×94 結構。與 EUC 編碼不同,ASCII 位元組可以出現在這些編碼中雙位元組程式碼的第二個位元組中,這在 EUC 編碼的常見擴充套件中也是如此(GBK 擴充套件了 euc-cn,統一韓文程式碼擴充套件了 euc-kr)。
Unicode 標準試圖將所有現代字元編碼統一到一個一致的 16 位表示中。考慮一個頁面,其中有一個 16x16 的表格,其中填充了西歐語言(ISO 8859-1),下半部分是 ASCII 程式碼點。將其稱為“頁面 00”,並想象一本書,其中有 256 頁或更多這樣的頁面(其中包含各種其他字元,大多數是 CJK),那麼您就對 Unicode 標準有了相當清楚的認識,其中字元的程式碼位置是“U+”十六進位制(頁碼*256+單元格號碼),例如,U+20A4 是英鎊符號的一種版本。Unicode 由計算機行業發起(www.unicode.org),與 ISO 10646 一同發展,ISO 10646 是一種並行標準,提供了一種最多 31 位的編碼(留一位用於奇偶校驗?),範圍相同。軟體必須允許 Unicode 字串適合 i18n。從 Unicode 3.1 版本開始,16 位限制被超越,適用於一些罕見的書寫系統,也適用於 CJK 統一表意文字擴充套件 B——顯然,即使 65536 個程式碼位置也不夠。Unicode 3.1 中的總數為 94,140 個編碼字元,其中 70,207 個是統一漢字;第二大組是 14000 多個韓文。而且這個數字還在不斷增長。
UTF-8 旨在涵蓋 7 位 ASCII、Unicode 和 ISO 10646。字元表示為 1..6 個八位位元組序列——在字元集業務中稱為八位位元組——(對於 ASCII:1,對於 Unicode:2..4),如下所示
- ASCII 0x00..0x7F(Unicode 頁面 0,左半部分):0x00..0x7F。沒有變化。
- Unicode,頁面 00..07:2 個位元組,
110aaabb 10bbbbbb,其中 aaa 是頁碼的最後幾位,bb.. 是第二個 Unicode 位元組的位。這些頁面涵蓋了歐洲/擴充套件拉丁語、希臘語、西里爾語、亞美尼亞語、希伯來語、阿拉伯語。 - Unicode,頁面 08..FE:3 個位元組,
1110aaaa 10aaaabb 10bbbbbb。這些涵蓋了基本多語言平面的其餘部分,包括韓文、日文漢字等等。這意味著東亞文字在 UTF-8 中比純 16 位 Unicode 長 50%。 - Unicode,補充平面:4 個位元組,
11110ppp 10ppaaaa 10aaaabb 10bbbbbb。這些不是 Unicode 的原始設計的一部分(只有 ISO 10646),但當 Unicode 標準變得清晰時,一個平面不足以實現 Unicode 的目標,它們就被新增到 Unicode 標準中。它們主要涵蓋表情符號、古代書寫系統、利基書寫系統、大量的模糊日文漢字/漢字,以及一些在 Unicode 的原始設計之後才出現的非常新的書寫系統。 - 超出 Unicode 的 ISO 10646 程式碼:4..6 個位元組。由於當前的審批流程透過防止在 17 個 Unicode 平面之外分配 ISO 10646 來使這些標準保持同步,因此可以保證在可預見的未來這些程式碼不存在。
UTF-8 的一般原則是,第一個位元組要麼是單位元組字元(如果小於 0x80),要麼透過第一個 0 之前的 1 的數量來指示多位元組程式碼的長度,然後用資料位填充。所有其他位元組以位 10 開頭,然後用 6 個數據位填充。由此得出,UTF-8 編碼中的位元組位於不同的範圍內。
00..7F - plain ASCII 80..BF - non-initial bytes of multibyte code C2..FD - initial bytes of multibyte code (C0, C1 are not legal!) FE, FF - never used, so can be used to detect a UTF-16 byte-order mark (and thus, a non-UTF-8 file).
初始位元組和非初始位元組之間的區別有助於進行合理性檢查,或與丟失資料重新同步。此外,它獨立於位元組順序(與 UCS-16 相反,見下文)。但是,Tcl 將這些 UTF-8 細節遮蔽在我們面前:字元就是字元,無論它是 7 位、16 位還是(將來)更多。
位元組序列 EF BB BF 是 \uFEFF 的 UTF-8 等效項,由 Windows 記事本檢測,當檔案以這三個位元組開頭時,記事本會切換到 UTF-8 編碼,並在以 UTF-8 形式儲存檔案時寫入它們。這並不總是用於其他地方,但通常會覆蓋檔案以它開頭的其他宣告的字元編碼。
UCS-2 表示(在 Tcl 中稱為“unicode”編碼)更容易解釋:每個字元程式碼都寫為一個 16 位“短”無符號整數。實際的複雜之處在於構成“短”的兩個記憶體位元組可以排列成“大端”(Motorola、Sparc)或“小端”(Intel)位元組順序。因此,為 Unicode 定義了以下規則
- 程式碼點 U+FEFF 被定義為位元組順序標記(BOM),後來改名為“零寬度不換行空格”,儘管實際上將其用於其次要空格角色現在被認為是過時的。
- 程式碼點 U+FFFE(以及 FFFF)是保證的非字元,永遠不會是有效的 Unicode 字元。它們旨在用作哨兵,或用於其他內部用途,以及用於檢測位元組順序標記是否被錯誤讀取。
這樣,一個 Unicode 閱讀應用程式(即使是記事本/W2k)也可以輕鬆地檢測到當它遇到位元組序列 FFFE 時出現問題,並交換接下來的位元組對——一種處理不同位元組順序的最小且優雅的方法。
雖然 Unicode 最初旨在完全適應 UCS-2,而整個 ISO 10646 需要一個 32 位“長”(稱為 UCS-4 或 UTF-32),但這種區別後來被廢除了,因為一個十六位平面不再被認為足以實現 Unicode 的目標。因此,十六個“補充”平面被新增到 Unicode 中,原始的 16 位平面被保留為“平面 0”或“基本多語言平面”。為了在預期為 UCS-2 流的介面中使用來自補充平面的字元,範圍 U+D800–U+DFFF 保證永遠不會用於 Unicode 字元。這允許補充平面中的字元在其他情況下以 UCS-2 流的形式明確表示,這被稱為 UTF-16。
補充字元在 big-endian UTF-16 中表示如下,其中 ssss 表示平面號減 1。在 little-endian 中,前兩個位元組被交換,後兩個位元組被交換,整個序列不會反轉。這是因為它被視為兩個 UCS-2 字元的序列。
110110ss ssaaaaaa 110111aa bbbbbbbb
對於 XML,編碼自識別透過開頭標籤中的編碼屬性定義。但這隻對可以被視為 ASCII 的文件有用,直到那時,因此必須預先檢測 UTF-16/UCS-2 或以其他方式指示。
從 Tcl 8.1 開始,i18n 支援被引入字串處理,明智地決定
- 使用 Unicode 作為通用字元集
- 使用 UTF-8 作為標準內部編碼
- 提供對許多其他正在使用的編碼的轉換支援。
但是,由於不等長的位元組序列使簡單任務(如索引字串或確定其字元長度)變得更加複雜,因此在這種情況下,內部表示將轉換為固定長度的 16 位 UCS-16。(這帶來了最近的 Unicode 跨越 16 位障礙的新問題……當實際使用證明這一點時,這將不得不更改為 UCS-32,或每個字元 4 個位元組。)
因此,並非所有 i18n 問題都對使用者自動解決。仍然需要分析看似簡單的任務,如大寫轉換(土耳其帶點/不帶點 I 構成異常)或排序(“整理順序”不一定與 Unicode 的數字順序相同,如 lsort 預設情況下會應用),如果需要更正確的行為,則編寫自定義例程。其他與區域設定相關的 i18n 問題,如數字/貨幣格式、日期/時間處理,也屬於這一組。我建議從 Tcl 提供的預設值開始,如有必要,根據需要自定義外觀。如果交換了本地化數字資料,一方使用點,另一方使用逗號作為小數點,那麼國際資料交換將受到嚴重阻礙……
嚴格來說,Tcl 實現“違反了 UTF-8 規範,該規範明確禁止字元的非規範表示,並要求輸入中格式錯誤的 UTF-8 序列為錯誤。……我認為這是一個優勢。但規範說“必須”,所以至少在技術上我們是不符合規範的。”(Kevin B. Kenny 在 Tcl 聊天中,2003-05-13)
如果文字資料在您的 Tcl 指令碼內部,您只需要知道 \uxxxx 表示法,它被替換為 Unicode U+xxxx(十六進位制)的字元。這種表示法可以在 Tcl 替換髮生的地方使用,甚至在花括號中的正則表示式和字串對映對列表中使用;否則,您可以透過對相關字串進行 substing 來強制使用它。
為了證明例如 scan 透明地工作,這裡有一個單行程式碼,將任何 Unicode 字元格式化為 HTML 十六進位制實體
proc c2html c {format "&#x%4.4x;" [scan $c %c]}
相反,它需要更多的行
proc html2u string {
while {[regexp {&#[xX]([0-9A-Fa-f]+);} $string matched hex]} {
regsub -all $matched $string [format %c 0x$hex] string
}
set string
}
% html2u "this is a &x20ac; sign"
this is a € sign
對於所有其他目的,兩個命令基本上提供了所有 i18n 支援
fconfigure $ch -encoding $e
如果與系統編碼不同,則為開啟的通道(檔案或套接字)啟用從/到編碼 e 的轉換;
encoding convertfrom/to $e $string
正如它所說,另一個編碼始終是 Unicode。
例如,我可以輕鬆地使用以下命令從十六進位制轉儲中解碼位元組 EF BB BF
format %x [encoding convertfrom utf-8 \xef\xbb\xbf]
在一個互動式的 tclsh 中,發現它代表了著名的位元組順序標記 FEFF。在 Tcl 內部,(幾乎)所有內容都是 Unicode 字串。所有與作業系統的通訊都使用“系統編碼”完成,您可以使用 [encoding system] 命令查詢(但最好不要更改)它。典型的值為歐洲 Linux 上的 iso8859-1 或 -15,以及歐洲 Windows 上的 cp1252。
內省:使用以下命令找出您的安裝中有哪些編碼可用
encoding names
您可以透過生成一個 .enc 檔案並將其複製到其他 .enc 檔案所在的目錄 lib/tcl8.4/encoding(或類似目錄)中來新增新的編碼。有關編碼檔案格式(它們是文字檔案,主要由十六進位制數字組成),請參閱手冊頁 http://www.tcl.tk/man/tcl8.4/TclLib/Encoding.htm 。您的 .enc 檔案的基名(不帶 .enc 副檔名)將是它可以被定址的名稱,例如,對於編碼 iso4711,將檔案命名為 iso4711.enc。
最後,msgcat 包支援應用程式的本地化(“l10n”),它允許訊息目錄用於將字串(通常用於 GUI 顯示)從基語言(通常為英語)翻譯成當前區域設定選擇的目標語言。例如,要為法國本地化的應用程式可能包含一個檔案 en_fr.msg,為簡單起見,它只有一行
msgcat::mcset fr File Fichier
在應用程式本身中,您只需要
package require msgcat namespace import msgcat::mc msgcat::mclocale fr ;#(1) #... pack [button .b -text [mc File]]
讓按鈕顯示“File”的本地化文字,即“Fichier”,該文字是從訊息目錄中獲取的。對於其他區域設定,只需要透過從基語言進行翻譯來生成新的訊息目錄。通常,區域設定資訊可能來自環境 (LANG) 或登錄檔變數,而不是像 (1) 中那樣顯式設定。
在顯示器或印表機上渲染國際字串可能會帶來最大的問題。首先,您需要包含相關字元的字型。幸運的是,越來越多的包含國際字元的字型可用,Bitstream Cyberbit 是先驅者之一,它包含大約 40000 個字形,並且在一段時間內可以在網路上免費下載。Microsoft 的 Tahoma 字型也增加了對大多數字母書寫系統的支援,包括阿拉伯語。與 Windows 2000 一起提供的 Arial Unicode MS 包含了 Unicode 中幾乎所有的字元,因此即使是簡單的記事本也可以使用它變得真正國際化。
但擁有一個好的字型還不夠。雖然記憶體中的字串按邏輯順序排列,地址從文字的開頭到結尾遞增,但它們可能需要以其他方式渲染,將變音符號移到前一個字元的不同位置,或者對於從右到左(“r2l”)書寫的語言來說最明顯:阿拉伯語、希伯來語。(Tk 仍然缺乏自動的“bidi”方向處理,因此 r2l 字串必須在記憶體中“錯誤地”定向以在渲染時顯示正確——請參閱 Wiki 上的簡單阿拉伯語渲染器。)
正確的 bidi 處理也會影響游標移動、行對齊和換行。從右到左前進的垂直線在日本和臺灣很流行——如果你必須渲染蒙古語,這是必須的。
梵文等印度文字是包含大約 40 個字元的字母,但子音和母音的順序在渲染時部分顛倒,子音群必須渲染為包含兩個或多個字元的連字——純單個字母對印度人來說看起來非常難看。一種印度文字的字型已經包含幾百個字形。不幸的是,印度連字不包含在 Unicode 中(而阿拉伯語連字包含在內),因此各種供應商標準適用於對這些連字進行編碼。

這裡有一個小指令碼,它可以顯示您的系統可以使用的哪些奇異字元。它建立一個文字視窗,並嘗試為指定的語言顯示一些示例文字(螢幕截圖來自 PocketPC 的 Bitstream Cyberbit 字型)
pack [text .t -font {Helvetica 16}]
.t insert end "
Arabic \uFE94\uFEF4\uFE91\uFEAE\uFECC\uFEDF\uFE8D\uFE94\uFEE4\uFEE0\uFEDC\uFEDF\uFE8D
Trad. Chinese \u4E2D\u570B\u7684\u6F22\u5B57
Simplified Chinese \u6C49\u8BED
Greek \u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AE\
\u03B3\u03BB\u03CE\u03C3\u03C3\u03B1
Hebrew \u05DD\u05D9\u05DC\u05E9\u05D5\u05E8\u05D9\
\u05DC\u05D9\u05D0\u05E8\u05E9\u05D9
Japanese \u65E5\u672C\u8A9E\u306E\u3072\u3089\u304C\u306A,\
\u6F22\u5B57\u3068\u30AB\u30BF\u30AB\u30CA
Korean \uB300\uD55C\uBBFC\uAD6D\uC758 \uD55C\uAE00 (\u3CA2\u3498)
Russian \u0420\u0443\u0441\u0441\u043A\u0438\u0439\
\u044F\u0437\u044B\u043A
"
沒有指定字型或大小,因此您看到的是純預設值(並注意 Tk 如何設法找到字元)。然後,您可以為想要檢視的字型配置文字視窗小部件。
要將鍵盤上看不到的奇異字元輸入到機器中,它們可以在最低級別上指定為轉義序列,例如“\u2345”。但大多數使用者輸入將來自鍵盤,在不同的國家/地區存在著許多鍵盤佈局。在 CJK 國家/地區,鍵盤和字元之間還有一個單獨的編碼級別:按鍵,它可能代表字元的發音或幾何成分,被收集到一個緩衝區中,並在有足夠上下文可用時(通常由螢幕選單支援以解決歧義)轉換為目的碼。
最後,螢幕上的“虛擬鍵盤”,使用者可以透過滑鼠點選選擇字元,對於不經常使用罕見字元的使用者來說尤其有用,因為物理鍵盤不會提示哪個鍵對映到哪個程式碼。這可以透過一組按鈕實現,或者最小限度地使用一個畫布來容納提供的字元作為文字專案,以及繫結到<1>,這樣點選字元就會將它的程式碼插入具有鍵盤焦點的視窗小部件中。參見 iKey:一個微小的多語言鍵盤。
術語“輸入法”通常用於作業系統特定的 i18n 支援,但我沒有這方面的經驗,因為我是在德語 Windows 安裝環境下進行 i18n 的。到目前為止,我對手工製作的純 Tcl/Tk 解決方案完全滿意——參見維基上的 taiku。
Lish 家族是一組音譯,旨在將低階的 7 位 ASCII 字串轉換為一些主要非拉丁文字系統中的相應 Unicode 字串。這個名字來自常見的字尾“lish”,如英語,它實際上是這個家族的中性元素,忠實地返回它的輸入;-) 一些經驗法則
- 一個 *lish 字元應該在適用時明確地對映到一個目標字元
- 一個目標字母應該在適用時由一個 *lish 字母 (A-Za-z) 表示。特殊字元和數字應該避免用於編碼字母
- 對映應該是直觀的,或者遵循既定的做法
- 在區分大小寫的語言中,對應於大寫和小寫字母的替代字元也應該在較低的 ASCII 中區分大小寫。
Tclers' Wiki http://mini.net/tcl/ 提供了 Lish 家族的成員,可以複製貼上。我經常使用的是
- Arblish,它執行上下文字形選擇和從右到左轉換;
- Greeklish;
- Hanglish 用於韓語韓文,它根據首字母-母音-尾字母計算 Unicode;
- Ruslish 用於西裡爾字母。
呼叫示例,返回指定輸入的 Unicode
arblish dby w Abw Zby greeklish Aqh'nai hanglish se-qul heblish irwsliM ruslish Moskva i Leningrad
這一切都始於 Greeklish,它不是我的發明,而是希臘人在網際網路上用來在沒有希臘字型或字元集支援的情況下書寫希臘語的。我只是擴充套件了我發現的慣例,用尾隨的撇號來標記重音母音(所以它不再是嚴格的 1:1 音譯)。特別注意將詞尾的“s”轉換為“c”,這樣就可以產生最終的 sigma。以下是程式碼
proc greeklish str {
regsub -all {s([ \t\n.,:;])} $str {c\1} str
string map {
A' \u386 E' \u388 H' \u389 I' \u38a O' \u38c U' \u38e W' \u38f
a' \u3ac e' \u3ad h' \u3ae i' \u3af o' \u3cc u' \u3cd w' \u3ce
A \u391 B \u392 G \u393 D \u394 E \u395 Z \u396 H \u397 Q \u398
I \u399 K \u39a L \u39b M \u39c N \u39d J \u39e O \u39f P \u3a0
R \u3a1 S \u3a3 T \u3a4 U \u3a5 F \u3a6 X \u3a7 Y \u3a8 W \u3a9
a \u3b1 b \u3b2 g \u3b3 d \u3b4 e \u3b5 z \u3b6 h \u3b7 q \u3b8
i \u3b9 k \u3ba l \u3bb m \u3bc n \u3bd j \u3be o \u3bf p \u3c0
r \u3c1 c \u3c2 s \u3c3 t \u3c4 u \u3c5 f \u3c6 x \u3c7 y \u3c8
w \u3c9 ";" \u387 ? ";"
} $str
}
測試
% greeklish Aqh'nai Αθήναι % greeklish "eis thn po'lin" εις την πόλιν
儘管韓語韓文有數千個音節字元,但仍然可以從音節的拼寫中計算出 Unicode,反之亦然。以下是方法
proc hangul2hanglish s {
set lead {g gg n d dd r m b bb s ss "" j jj c k t p h}
set vowel {a ae ya yai e ei ye yei o oa oai oi yo u ue uei ui yu w wi i}
set tail {"" g gg gs n nj nh d l lg lm lb ls lt lp lh m b bs s ss ng j c k t p h}
set res ""
foreach c [split $s ""] {
scan $c %c cnum
if {$cnum>=0xAC00 && $cnum<0xD7A3} {
incr cnum -0xAC00
set l [expr {$cnum / (28*21)}]
set v [expr {($cnum/28) % 21}]
set t [expr {$cnum % 28}]
append res [lindex $lead $l ]
append res [lindex $vowel $v]
append res "[lindex $tail $t] "
} else {append res $c}
}
set res
}
proc hanglish2uc hanglish {
set L ""; set V "" ;# in case regexp doesn't hit
set hanglish [string map {
AE R SH S R L NG Q YE X YAI F AI R YA V YO Y YU Z VI F
} [string toupper $hanglish]]
regexp {^([GNDLMBSQJCKTPH]+)?([ARVFEIXOYUZW]+)([GNDLMBSQJCKTPH]*)$} \
$hanglish -> L V T ;# lead cons.-vowel-trail cons.
if {$L==""} {set L Q}
if {$V==""} {return $hanglish}
set l [lsearch {G GG N D DD L M B BB S SS Q J JJ C K T P H} $L]
set v [lsearch {A R V F E EI X XI O OA OR OI Y U UE UEI UI Z W WI I} $V]
set t [lsearch {"" G GG GS N NJ NH D L LG LM LB LS LT LP LH \
M B BS S SS Q J C K T P H} $T] ;# trailing consonants
if {[min $l $v $t] < 0} {return $hanglish}
format %c [expr {$l*21*28 + $v*28 + $t + 0xAC00}]
}
proc min args {lindex [lsort -real $args] 0}
proc hanglish argl {
set res ""
foreach i $argl {
foreach j [split $i -] {append res [hanglish2uc $j]}
}
append res " "
}
排序是指“根據定義的優先順序規則對字元或寬字元字串進行邏輯排序。這些規則在排序元素之間識別排序順序,以及可用於對由多個排序元素組成的字串進行排序的其他規則。”
Tcl 的 lsort 按數字 Unicode 值進行排序,這在某些區域設定中可能不正確。例如,在葡萄牙語中,重音字母應該像沒有重音一樣排序,但在 Unicode 序列中排在“z”之後。
以下簡單的程式碼接收一個對映,其中可以列出排序差異,如 {from to from to ...},對對映後的專案進行排序,然後只檢索原始元素
proc collatesort {list map} {
set l2 {}
foreach e $list {
lappend l2 [list $e [string map $map $e]]
}
set res {}
foreach e [lsort -index 1 $l2] {lappend res [lindex $e 0]}
set res
}
測試,葡萄牙語
% collatesort {ab ãc ãd ae} {ã a}
ab ãc ãd ae
西班牙語 (ll 排在 lz 之後)
% collatesort {llano luxación leche} {ll lzz}
leche luxación llano
德語 (變音字母排序,就像“ä”是“ae”一樣)
% lsort {Bar Bär Bor}
Bar Bor Bär
% collatesort {Bar Bär Bor} {ä ae}
Bär Bar Bor