UTF-8 and Unicode FAQ for Unix/Linux

 

UTF-8 and Unicode FAQ for Unix/Linux

作者:Markus Kuhn

 

這是一篇介紹如何在POSIX系統下(Linux,Unix)使用Unicode/UTF-8的文章,它不僅針對一般的使用者做了介紹性的說明,而且對於那些處理字符編碼的專業開發者提供了詳細的參考資料(沒錯,你不用再google出來一堆相互抄襲的垃圾文章了,直接看這篇文章,你就能獲得所有關於UTF-8和Unicode的信息)。

Unicode的出現是爲了取代ASCII, ISO 8859 和各種EUC編碼。它不僅能讓使用者處理地球上幾乎所有的語言和符號,而且還爲科學研究的信息交換提供了一套複雜的數學和專業符號。

採用UTF-8的編碼,Unicode就能在那些專爲ASCII碼而實際的系統上(如Unix)方便的使用,並向後兼容。UTF-8是Unicode在類似於Unix,Linux等系統上的一種應用。下面介紹就是爲了你能熟練的掌握它,並使你的軟件能順利的支持UTF-8.

 

本來想這自己翻譯一下這篇文章,結果google一下,發現有前輩已經翻譯過了:)

 

內容:

 

搞了半天有前輩已經把這篇文章翻譯好了:http://www.linuxforum.net/books/UTF-8-Unicode.html

UTF-8 and Unicode FAQ

by Markus Kuhn

中國LINUX論壇翻譯小組 xLoneStar[譯] 2000年2月

這篇文章說明了在 POSIX 系統 (Linux,Unix) 上使用 Unicode/UTF-8 所需要的信息. 在將來不遠的幾年裏, Unicode 已經很接近於取代 ASCII 與 Latin-1 編碼的位置了. 它不僅允許你處理處理事實上存在於地球上的任何語言文字, 而且提供了一個全面的數學與技術符號集, 因此可以簡化科學信息交換.

UTF-8 編碼提供了一種簡便而向後兼容的方法, 使得那種完全圍繞 ASCII 設計的操作系統, 比如 Unix, 也可以使用 Unicode. UTF-8 就是 Unix, Linux 已經類似的系統使用 Unicode 的方式. 現在是你瞭解它的時候了.

什麼是 UCS 和 ISO 10646?

國際標準 ISO 10646 定義了 通用字符集 (Universal Character Set, UCS). UCS 是所有其他字符集標準的一個超集. 它保證與其他字符集是雙向兼容的. 就是說, 如果你將任何文本字符串翻譯到 UCS格式, 然後再翻譯回原編碼, 你不會丟失任何信息.

UCS 包含了用於表達所有已知語言的字符. 不僅包括拉丁語,希臘語, 斯拉夫語,希伯來語,阿拉伯語,亞美尼亞語和喬治亞語的描述, 還包括中文, 日文和韓文這樣的象形文字, 以及 平假名, 片假名, 孟加拉語, 旁遮普語果魯穆奇字符(Gurmukhi), 泰米爾語, 印.埃納德語(Kannada), Malayalam, 泰國語, 老撾語, 漢語拼音(Bopomofo), Hangul, Devangari, Gujarati, Oriya, Telugu 以及其他數也數不清的語. 對於還沒有加入的語言, 由於正在研究怎樣在計算機中最好地編碼它們, 因而最終它們都將被加入. 這些語言包括 Tibetian, 高棉語, Runic(古代北歐文字), 埃塞俄比亞語, 其他象形文字, 以及各種各樣的印-歐語系的語言, 還包括挑選出來的藝術語言比如 Tengwar, Cirth 和 克林貢語(Klingon). UCS 還包括大量的圖形的, 印刷用的, 數學用的和科學用的符號, 包括所有由 TeX, Postscript, MS-DOS,MS-Windows, Macintosh, OCR 字體, 以及許多其他字處理和出版系統提供的字符.

ISO 10646 定義了一個 31 位的字符集. 然而, 在這巨大的編碼空間中, 迄今爲止只分配了前 65534 個碼位 (0x0000 到 0xFFFD). 這個 UCS 的 16位子集稱爲 基本多語言面 (Basic Multilingual Plane, BMP). 將被編碼在 16 位 BMP 以外的字符都屬於非常特殊的字符(比如象形文字), 且只有專家在歷史和科學領域裏纔會用到它們. 按當前的計劃, 將來也許再也不會有字符被分配到從 0x000000 到 0x10FFFF 這個覆蓋了超過 100 萬個潛在的未來字符的 21 位的編碼空間以外去了. ISO 10646-1 標準第一次發表於 1993 年, 定義了字符集與 BMP 中內容的架構. 定義 BMP 以外的字符編碼的第二部分 ISO 10646-2 正在準備中, 但也許要過好幾年才能完成. 新的字符仍源源不斷地加入到 BMP 中, 但已經存在的字符是穩定的且不會再改變了.

UCS 不僅給每個字符分配一個代碼, 而且賦予了一個正式的名字. 表示一個 UCS 或 Unicode 值的十六進制數, 通常在前面加上 "U+", 就象 U+0041 代表字符"拉丁大寫字母A". UCS 字符 U+0000 到 U+007F 與 US-ASCII(ISO 646) 是一致的, U+0000 到 U+00FF 與 ISO 8859-1(Latin-1) 也是一致的. 從 U+E000 到 U+F8FF, 已經 BMP 以外的大範圍的編碼是爲私用保留的.

什麼是組合字符?

UCS裏有些編碼點分配給了 組合字符.它們類似於打字機上的無間隔重音鍵. 單個的組合字符不是一個完整的字符. 它是一個類似於重音符或其他指示標記, 加在前一個字符後面. 因而, 重音符可以加在任何字符後面. 那些最重要的被加重的字符, 就象普通語言的正字法(orthographies of common languages)裏用到的那種, 在 UCS 裏都有自己的位置, 以確保同老的字符集的向後兼容性. 既有自己的編碼位置, 又可以表示爲一個普通字符跟隨一個組合字符的被加重字符, 被稱爲 預作字符(precomposed characters). UCS 裏的預作字符是爲了同沒有預作字符的舊編碼, 比如 ISO 8859, 保持向後兼容性而設的. 組合字符機制允許在任何字符後加上重音符或其他指示標記, 這在科學符號中特別有用, 比如數學方程式和國際音標字母, 可能會需要在一個基本字符後組合上一個或多個指示標記.

組合字符跟隨着被修飾的字符. 比如, 德語中的元音變音字符 ("拉丁大寫字母A 加上分音符"), 既可以表示爲 UCS 碼 U+00C4 的預作字符, 也可以表示成一個普通 "拉丁大寫字母A" 跟着一個"組合分音符":U+0041 U+0308 這樣的組合. 當需要堆疊多個重音符, 或在一個基本字符的上面和下面都要加上組合標記時, 可以使用多個組合字符. 比如在泰國文中, 一個基本字符最多可加上兩個組合字符.

什麼是 UCS 實現級別?

不是所有的系統都需要支持象組合字符這樣的 UCS 裏所有的先進機制. 因此 ISO 10646 指定了下列三種實現級別:

級別1
不支持組合字符和 Hangul Jamo 字符 (一種特別的, 更加複雜的韓國文的編碼, 使用兩個或三個子字符來編碼一個韓文音節)
級別2
類似於級別1, 但在某些文字中, 允許一列固定的組合字符 (例如, 希伯來文, 阿拉伯文, Devangari, 孟加拉語, 果魯穆奇語, Gujarati, Oriya, 泰米爾語, Telugo, 印.埃納德語, Malayalam, 泰國語和老撾語). 如果沒有這最起碼的幾個組合字符, UCS 就不能完整地表達這些語言.
級別3
支持所有的 UCS 字符, 例如數學家可以在任意一個字符上加上一個 tilde(顎化符號,西班牙語字母上面的~)或一個箭頭(或兩者都加).

什麼是 Unicode?

歷史上, 有兩個獨立的, 創立單一字符集的嘗試. 一個是國際標準化組織(ISO)的 ISO 10646 項目, 另一個是由(一開始大多是美國的)多語言軟件製造商組成的協會組織的 Unicode 項目. 幸運的是, 1991年前後, 兩個項目的參與者都認識到, 世界不需要兩個不同的單一字符集. 它們合併雙方的工作成果, 併爲創立一個單一編碼表而協同工作. 兩個項目仍都存在並獨立地公佈各自的標準, 但 Unicode 協會和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 10646 標準的碼錶兼容, 並緊密地共同調整任何未來的擴展.

那麼 Unicode 和 ISO 10646 不同在什麼地方?

Unicode 協會公佈的 Unicode 標準 嚴密地包含了 ISO 10646-1 實現級別3的基本多語言面. 在兩個標準裏所有的字符都在相同的位置並且有相同的名字.

Unicode 標準額外定義了許多與字符有關的語義符號學, 一般而言是對於實現高質量的印刷出版系統的更好的參考. Unicode 詳細說明了繪製某些語言(比如阿拉伯語)表達形式的算法, 處理雙向文字(比如拉丁與希伯來文混合文字)的算法和 排序與字符串比較 所需的算法, 以及其他許多東西.

另一方面, ISO 10646 標準, 就象廣爲人知的 ISO 8859 標準一樣, 只不過是一個簡單的字符集表. 它指定了一些與標準有關的術語, 定義了一些編碼的別名, 幷包括了規範說明, 指定了怎樣使用 UCS 連接其他 ISO 標準的實現, 比如 ISO 6429 和 ISO 2022. 還有一些與 ISO 緊密相關的, 比如 ISO 14651 是關於 UCS 字符串排序的.

考慮到 Unicode 標準有一個易記的名字, 且在任何好的書店裏的 Addison-Wesley 裏有, 只花費 ISO 版本的一小部分, 且包括更多的輔助信息, 因而它成爲使用廣泛得多的參考也就不足爲奇了. 然而, 一般認爲, 用於打印 ISO 10646-1 標準的字體在某些方面的質量要高於用於打印 Unicode 2.0的. 專業字體設計者總是被建議說要兩個標準都實現, 但一些提供的樣例字形有顯著的區別. ISO 10646-1 標準同樣使用四種不同的風格變體來顯示表意文字如中文, 日文和韓文 (CJK), 而 Unicode 2.0 的表裏只有中文的變體. 這導致了普遍的認爲 Unicode 對日本用戶來說是不可接收的傳說, 儘管是錯誤的.

什麼是 UTF-8?

首先 UCS 和 Unicode 只是分配整數給字符的編碼表. 現在存在好幾種將一串字符表示爲一串字節的方法. 最顯而易見的兩種方法是將 Unicode 文本存儲爲 2 個 或 4 個字節序列的串. 這兩種方法的正式名稱分別爲 UCS-2 和 UCS-4. 除非另外指定, 否則大多數的字節都是這樣的(Bigendian convention). 將一個 ASCII 或 Latin-1 的文件轉換成 UCS-2 只需簡單地在每個 ASCII 字節前插入 0x00. 如果要轉換成 UCS-4, 則必須在每個 ASCII 字節前插入三個 0x00.

在 Unix 下使用 UCS-2 (或 UCS-4) 會導致非常嚴重的問題. 用這些編碼的字符串會包含一些特殊的字符, 比如 '/0' 或 '/', 它們在 文件名和其他 C 庫函數參數裏都有特別的含義. 另外, 大多數使用 ASCII 文件的 UNIX 下的工具, 如果不進行重大修改是無法讀取 16 位的字符的. 基於這些原因, 在文件名, 文本文件, 環境變量等地方, UCS-2 不適合作爲 Unicode 的外部編碼.

在 ISO 10646-1 Annex RRFC 2279 裏定義的 UTF-8 編碼沒有這些問題. 它是在 Unix 風格的操作系統下使用 Unicode 的明顯的方法.

UTF-8 有一下特性:

  • UCS 字符 U+0000 到 U+007F (ASCII) 被編碼爲字節 0x00 到 0x7F (ASCII 兼容). 這意味着只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 兩種編碼方式下是一樣的.
  • 所有 >U+007F 的 UCS 字符被編碼爲一個多個字節的串, 每個字節都有標記位集. 因此, ASCII 字節 (0x00-0x7F) 不可能作爲任何其他字符的一部分.
  • 表示非 ASCII 字符的多字節串的第一個字節總是在 0xC0 到 0xFD 的範圍裏, 並指出這個字符包含多少個字節. 多字節串的其餘字節都在 0x80 到 0xBF 範圍裏. 這使得重新同步非常容易, 並使編碼無國界, 且很少受丟失字節的影響.
  • 可以編入所有可能的 231個 UCS 代碼
  • UTF-8 編碼字符理論上可以最多到 6 個字節長, 然而 16 位 BMP 字符最多只用到 3 字節長.
  • Bigendian UCS-4 字節串的排列順序是預定的.
  • 字節 0xFE 和 0xFF 在 UTF-8 編碼中從未用到.

下列字節串用來表示一個字符. 用到哪個串取決於該字符在 Unicode 中的序號.

U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

xxx 的位置由字符編碼數的二進制表示的位填入. 越靠右的 x 具有越少的特殊意義. 只用最短的那個足夠表達一個字符編碼數的多字節串. 注意在多字節串中, 第一個字節的開頭"1"的數目就是整個串中字節的數目.

例如: Unicode 字符 U+00A9 = 1010 1001 (版權符號) 在 UTF-8 裏的編碼爲:

11000010 10101001 = 0xC2 0xA9

而字符 U+2260 = 0010 0010 0110 0000 (不等於) 編碼爲:

11100010 10001001 10100000 = 0xE2 0x89 0xA0

這種編碼的官方名字拼寫爲 UTF-8, 其中 UTF 代表 UCS Transformation Format. 請勿在任何文檔中用其他名字 (比如 utf8 或 UTF_8) 來表示 UTF-8, 當然除非你指的是一個變量名而不是這種編碼本身.

什麼編程語言支持 Unicode?

在大約 1993 年之後開發的大多數現代編程語言都有一個特別的數據類型, 叫做 Unicode/ISO 10646-1 字符. 在 Ada95 中叫 Wide_Character, 在 Java 中叫 char.

ISO C 也詳細說明了處理多字節編碼和寬字符 (wide characters) 的機制, 1994 年 9 月 Amendment 1 to ISO C 發表時又加入了更多. 這些機制主要是爲各類東亞編碼而設計的, 它們比處理 UCS 所需的要健壯得多. UTF-8 是 ISO C 標準調用多字節字符串的編碼的一個例子, wchar_t 類型可以用來存放 Unicode 字符.

在 Linux 下該如何使用 Unicode?

在 UTF-8 之前, 不同地區的 Linux 用戶使用各種各樣的 ASCII 擴展. 最普遍的歐洲編碼是 ISO 8859-1 和 ISO 8859-2, 希臘編碼 ISO 8859-7, 俄國編碼 KOI-8, 日本編碼 EUC 和 Shift-JIS, 等等. 這使得 文件的交換非常困難, 且應用軟件必須特別關心這些編碼的不同之處.

最終, Unicode 將取代所有這些編碼, 主要通過 UTF-8 的形式. UTF-8 將應用在

  • 文本文件 (源代碼, HTML 文件, email 消息, 等等)
  • 文件名
  • 標準輸入與標準輸出, 管道
  • 環境變量
  • 剪切與粘貼選擇緩衝區
  • telnet, modem 和到終端模擬器的串口連接
  • 以及其他地方以前用ASCII來表示的字節串

在 UTF-8 模式下, 終端模擬器, 比如 xterm 或 Linux console driver, 將每次按鍵轉換成相應的 UTF-8 串, 然後發送到前臺進程的 stdin 裏. 類似的, 任何進程在 stdout 上的輸出都將發送到終端模擬器, 在那裏用一個 UTF-8 解碼器進行處理, 之後再用一種 16 位的字體顯示出來.

只有在功能完善的多語言字處理器包裏纔可能有完全的 Unicode 功能支持. 而廣泛用在 Linux 裏用於取代 ASCII 和其他 8 位字符集的方案則要簡單得多. 第一步, Linux 終端模擬器和命令行工具將只是轉變到 UTF-8. 這意味着只用到 級別1 的 ISO 10646-1 實現 (沒有組合字符), 且只支持那些不需要更多處理的語言象 拉丁, 希臘, 斯拉夫 和許多科學用符號. 在這個級別上, UCS 支持與 ISO 8859 支持類似, 唯一顯著的區別是現在我們有幾千種字符可以用了, 其中的字符可以用多字節串來表示.

總有一天 Linux 會當然地支持組合字符, 但即便如此, 對於組合字符串, 預作字符(如何可用的話)仍將是首選的. 更正式地, 在 Linux 下用 Unicode 對文本編碼的首選的方法應該是定義在 Unicode Technical Report #15 裏的 Normalization Form C.

在今後的一個階段, 人們可以考慮增加在日文和中文裏用到的雙字節字符的支持 (他們相對比較簡單), 組合字符支持, 甚至也許對從右至左書寫的語言如希伯來文 (他們可不是那麼簡單的) 的支持. 但對這些高級功能的支持不應該阻礙簡單的平板 UTF-8 在 拉丁, 希臘, 斯拉夫和科學用符號方面的快速應用, 以取代大量的歐洲 8 位編碼, 並提供一個象樣的科學用符號集.

我該怎樣修改我的軟件?

有兩種途徑可以支持 UTF-8, 我稱之爲軟轉換與硬轉換. 軟轉換時, 各處的數據均保存爲 UTF-8 形式, 因而需要修改的軟件很少. 在硬轉換時, 程序將讀入的 UTF-8 數據轉換成寬字符數組, 以在應用程序內部處理. 在輸出時, 再把字符串轉換回 UTF-8 形式.

大多數應用程序只用軟轉換就可以工作得很好了. 這使得將 UTF-8 引入 Unix 成爲切實可行的. 例如, 象 cat 和 echo 這樣的程序根本不需要修改. 他們仍然可以對輸入輸出的是 ISO 8859-2 還是 UTF-8 一無所知, 因爲它們只是搬運字節流而沒有處理它們. 它們只能識別 ASCII 字符和象 '/n' 這樣的控制碼, 而這在 UTF-8 下也沒有任何改變. 因此, 這些應用程序的 UTF-8 編碼與解碼將完全在終端模擬器裏完成.

而那些通過數字節數來獲知字符數量的程序則需要一些小修改. 在 UTF-8 模式下, 它們必須不數入 0x80 到 0xBF 範圍內的字節, 因爲這些只是跟隨字節, 它們本身並不是字符. 例如, ls 程序就必須要修改, 因爲它通過數文件名中字符數來排放給用戶的目錄表格佈局. 類似地, 所有的假定其輸出爲定寬字體, 並因此而格式化它們的程序, 必須學會怎樣數 UTF-8 文本中的字符數. 編輯器的功能, 如刪除單個字符, 必須要作輕微的修改, 以刪除可能屬於該字符的所有字節. 受影響有編輯器 (vi,emacs, 等等)以及使用 ncurses 庫的程序.

Linux 核心使用軟轉換也可以工作得很好, 只需要非常微小的修改以支持 UTF-8. 大多數處理字符串的核心功能 (例如: 文件名, 環境變量, 等等) 都不受影響. 下列地方也許必須修改:

  • 控制檯顯示與鍵盤驅動程序 (另一個 VT100 模擬器) 必須能編碼和解碼 UTF-8, 必須要起碼支持 Unicode 字符集的幾個子集. 從 Linux 1.2 起這些功能已經有了.
  • 外部文件系統驅動程序, 例如 VFAT 和 WinNT 必須轉換文件名字符編碼. UTF-8 已經加入可用的轉換選項的列表裏了, 因此 mount 命令必須告訴核心驅動程序用戶進程希望看到 UTF-8 文件名. 既然 VFAT 和 WinNT 無論如何至少已經用了 Unicode了, 那麼 UTF-8 在這裏就可以發揮其優勢, 以保證轉換中無信息損失.
  • POSIX 系統的 tty 驅動程序支持一種 "cooked" 模式, 有一些原始的行編輯功能. 爲了讓字符刪除功能工作正常, stty 必須在 tty 驅動程序裏設置 UTF-8 模式, 因此它就不會把 0x80 到 0xBF 範圍內的跟隨字符也數進去了. Bruno Haible 那裏已經有了一些 stty 和核心 tty 驅動 程序的 Linux 補丁 了.

C 對 Unicode 和 UTF-8 的支持

從 GNU glibc 2.1 開始, wchar_t 類型已經正式定爲只存放獨立於當前 locale 的, 32位的 ISO 10646 值. glibc 2.2 開始將完全支持 ISO C 中的多字節轉換函數 (wprintf(),mbstowcs(),等等), 這些函數可以用於在 wchar_t 和包括 UTF-8 在內的任何依賴於 locale 的多字節編碼間進行轉換.

例如, 你可以寫

  wprintf(L"Sch鰊e Gre!/n");

然後, 你的軟件將按照你的用戶在環境變量 LC_CTYPE (例如, en_US.UTF-8de_DE.ISO_8859-1) 中選擇的 locale 所指定的編碼來打印這段文字. 你的編譯器必須運行在與該 C 源文件所用編碼相應的 locale 中, 在目標文件中以上的寬字符串將改爲 wchar_t 字符串存儲. 在輸出時, 運行時庫將把 wchar_t 字符串轉換回與程序執行時的 locale 相應的編碼.

注意, 類似這樣的操作:

 

  char c = L"a"; 

只允許從 U+0000 到 U+007F (7 位 ASCII) 範圍裏的字符. 對於非 ASCII 字符, 不能直接從 wchar_tchar 轉換.

現在, 象 readline() 這樣的函數在 UTF-8 locale 下也能工作了.

怎樣激活 UTF-8 模式?

如果你的應用程序既支持 8 位字符集 (ISO 8859-*,KOI-8,等等), 也支持 UTF-8, 那麼它必須通過某種方法以得知是否應使用 UTF-8 模式. 幸運的是, 在未來的幾年裏, 人們將只使用 UTF-8, 因此你可以將它作爲默認, 但即使如此, 你還是得既支持傳統 8 位字符集, 也支持 UTF-8.

當前的應用程序使用許許多多的不同的命令行開關來激活它們各自的 UTF-8 模式, 例如:

  • xterm 命令行選項 "-u8" 和 X resource "XTerm*utf8:1"
  • gnat/gcc 命令行選項 "-gnatW8"
  • stty 命令行選項 "iutf8"
  • mined 命令行選項 "-U"
  • xemacs elisp 包裹 以在 UTF-8 和內部使用的 MULE 編碼間轉換
  • vim 'fileencoding' 選項
  • less 環境變量 LESSCHARSET=utf-8

記住每一個應用程序的命令行選項或其他配置方法是非常單調乏味的, 因此急需某種標準方法.

如果你在你的應用程序裏使用硬轉換, 並使用某種特定的 C 庫函數來處理外部字符編碼和內部使用的 wchar_t 編碼的轉換工作, 那麼 C 庫會幫你處理模式切換的問題. 你只需將環境變量 LC_CTYPE 設爲正確的 locale, 例如, 如果你使用 UTF-8, 那就是en.UTF-8, 而如果是 Latin-1, 並需要英語的轉換, 則設爲 en.ISO_8859-1.

然而, 大多數現存軟件的維護者選擇用軟轉換來代替, 而不使用 libc 的寬字符函數, 不僅因爲它們還未得到廣泛應用, 還因爲這會使得軟件進行大規模修改. 在這種情況下, 你的應用程序必須自己來獲知何時使用 UTF-8 模式. 一種方式是做以下工作:

按照環境變量 LC_ALL, LC_CTYPE, LANG 的順序, 尋找第一個有值的變量. 如果該值包含 UTF-8 子串 (也許是小寫或沒有"-") 則默認爲 UTF-8 模式 (仍然可以用命令行開關來重設), 因爲這個值可靠又恰當地指示了 C 庫應該使用一種 UTF-8 locale.

提供一個命令行選項 (或者如果是 X 客戶程序則用 X resource 的值) 將仍然是有用的, 可以用來重設由 LC_CTYPE 等環境變量指定的默認值.

我怎樣才能得到 UTF-8 版本的 xterm?

XFree86 裏帶的 xterm 版本最近已經由 Thomas E. Dickey 加入了支持 UTF-8 的擴展. 使用方法是, 獲取 xterm patch #119 (1999-10-16) 或更新版本, 用 "./configure --enable-wide-chars ; make" 來編譯, 然後用命令行選項 -u8 來調用 xterm, 使它將輸入輸出轉換爲 UTF-8. 在 UTF-8 模式裏使用一個 *-ISO10646-1 字體. 當你在 ISO 8859-1 模式裏時也可以使用 *-ISO10646-1 字體, 因爲 ISO 10646-1 字體與 ISO 8859-1 字體是完全向後兼容的.

新的支持 UTF-8 的 xterm 版本, 以及一些 ISO 10646-1 字體, 將被收錄入 XFree86 4.0 版裏.

xterm 支持組合字符嗎?

Xterm 當前只支持級別1的 ISO 10646-1, 就是說, 不提供組合字符的支持. 當前, 組合字符將被當作空格字符對待. xterm 將來的修訂版很有可能加入某些簡單的組合字符支持, 就是僅僅將那個有一個或多個組合字符的基字符加粗 (logical OR-ing). 對於在基線以下的和在小字符上方的重音符來說, 這樣處理的結果還是可以接受的. 對於象泰國文字體那樣使用特別設計的加粗字符的文字, 這樣處理也能工作的很好. 然而, 對於某些字體裏, 在較高的字符上方組合上的重音符, 特別是對於 "fixed" 字體族, 產生的結果就不完全令人滿意了. 因此, 在可用的地方, 應該繼續優先使用預作字符.

xterm 支持半寬與全寬 CJK 字體嗎?

Xterm 當前只支持那種所有字形都等寬的 cell-spaced 的字體. 將來的修訂版很有可能爲 CJK 語言加入半寬與全寬字符支持, 類似於 kterm 提供的那種. 如果選擇的普通字體是 X×Y 象素大小, 且寬字符模式是打開的, 那麼 xterm 會試圖裝入另外的一個 2X×Y 象素大小的字體 (同樣的 XLFD, 只是 AVERAGE_WIDTH 屬性的值翻倍). 它會用這個字體來顯示所有在 Unicode Technical Report #11 裏被分配了East Asian Wide (W)East Asian FullWidth (F) 寬度屬性的 Unicode 字符. 下面這個 C 函數用來測試一個 Unicode 字符是否是寬字符並需要用覆蓋兩個字符單元的字形來顯示:

  /* This function tests, whether the ISO 10646/Unicode character code
* ucs belongs into the East Asian Wide (W) or East Asian FullWidth
* (F) category as defined in Unicode Technical Report #11. In this
* case, the terminal emulator should represent the character using a
* a glyph from a double-wide font that covers two normal (Latin)
* character cells. */

int iswide(int ucs)
{
if (ucs < 0x1100)
return 0;

return
(ucs >= 0x1100 && ucs <= 0x115f) || /* Hangul Jamo */
(ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a &&
ucs != 0x303f) || /* CJK ... Yi */
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
(ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
(ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */
(ucs >= 0xffe0 && ucs <= 0xffe6);
}

某些 C 庫也提供了函數

  #include <wchar.h>
int wcwidth(wchar_t wc);
int wcswidth(const wchar_t *pwcs, size_t n);

用來測定該寬字符 wc 或由 pwcs 指向的字符串中的 n 個寬字符碼 (或者少於 n 個寬字符碼, 如果在 n 個寬字符碼之前遇到一個空寬字符的話) 所要求的列位置的數量. 這些函數定義在 Open Group 的 Single UNIX Specification 裏. 一個拉丁/希臘/斯拉夫/等等的字符要求一個列位置, 一個 CJK 象形文字要求兩個, 而一個組合字符要求零個.

最終 xterm 是否會支持從右到左的書寫?

此刻還沒有給 xterm 增加從右到左功能的計劃. 希伯來與阿拉伯用戶因此不得不靠應用程序在將希伯來文與阿拉伯文字符串送到終端前按左方向翻轉它們, 換句話說, 雙向處理必須在應用程序裏完成, 而不是在 xterm 裏. 至少, 希伯來與阿拉伯文在預作字形的可用性的形式上, 以及提示表格上的支持, 比 ISO 8859 要有所改進. 現在還遠沒有決定 xterm 是否支持雙向文字以及該怎樣工作. ISO 6429 = ECMA-48Unicode bidi algorithm 都提供了可供選擇的開始點. 也可以參考 ECMA Technical
Report TR/53
. Xterm 也不處理阿拉伯文, Hangul 或 印度文本的格式化算法, 而且現在還不太清楚在 VT100 模擬器裏處理是否可行和值得, 或者應該留給應用軟件去處理. 如果你打算在你的應用程序裏支持雙向文字輸出, 看一下 FriBidi, Dov Grobgeld 的 Unicode 雙向算法的自由實現.

我在哪兒能找到 ISO 10646-1 X11 字體?

在過去的幾個月裏出現了相當多的 X11 的 Unicode 字體, 並且還在快速增多.

  • Markus Kuhn 正和其他許多志願者一起工作於手動將舊的 -misc-fixed-*-iso8859-1 字體擴展到覆蓋所有的歐洲字符表 (拉丁, 希臘, 斯拉夫, 國際音標字母表. 數學與技術符號, 某些字體裏甚至有亞美尼亞語, 喬治亞語, 片假名等). 更多信息請參考 Unicode fonts and tools for X11 頁. 這些字體將與 XFree86 一起分發. 例如字體
      -misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso10646-1

    (舊的 xterm 的 fixed 缺省字體的一個擴展, 包括超過 3000 個字符) 已經是 XFree86 3.9 snapshot 的一部分了.

  • Markus 也做好了 X11R6.4 distribution 裏所有的 Adobe 和 B&H BDF 字體的 ISO 10646 版本. 這些字體已經包含了全部 Postscript 字體表 (大約 30 個額外的字符, 大部分也被 CP1252 MS-Windows 使用, 如 smart quotes, dashes 等), 在 ISO 8859-1 編碼下是沒有的. 它們在 ISO 10646-1 版本里是完全可用的.
  • XFree86 4.0 將攜帶一個集成的 TrueType 字體引擎, 這使得你的 X 應用程序可以將任何 Apple/Microsoft 字體用於 ISO 10646-1 編碼.
  • 將來的 XFree86 版本很有可能從分發版中去除大多數舊的 BDF 字體, 取而代之的是 ISO 10646-1 編碼的版本. X 服務器則會增加一個自動編碼轉換器, 只有當舊的 8 位軟件請求一個類似於 ISO 8859-* 編碼的字體時, 才虛擬地從 ISO 10646-1 字體文件中創建一個這樣的字體. 現代軟件應該優先地直接使用 ISO 10646-1 字體編碼.
  • ClearlyU (cu12) 是一個非常有用的 X11 的 12 點陣, 100 dpi 的 proportional ISO 10646-1 BDF 字體, 包含超過 3700 個字符, 由 Mark Leisher 提供 (樣例圖象).
  • Roman Czyborra 的 GNU Unicode font 項目工作於收集一個完整的與免費的 8×16/16×16 pixel Unicode 字體. 目前已經覆蓋了 34000 個字符.
  • etl-unicode 是一個 ISO 10646-1 BDF 字體, 由 Primoz Peterlin 提供.

Unicode X11 字體名字以 -ISO10646-1 結尾. 這個 X 邏輯字體描述器 (X Logical Font Descriptor, XLFD) 的 CHARSET_REGISTRY 和 CHARSET_ENCODING 域裏的值已經爲所有 Unicode 和 ISO 10646-1 的 16 位字體而正式地註冊了. 每個 *-ISO10646-1 字體都包含了整個 Unicode 字符集裏的某幾個子集, 而用戶必須弄清楚他們選擇的字體覆蓋哪幾個他們需要的字符子集.

*-ISO10646-1 字體通常也指定一個 DEFAULT_CHAR 值, 指向一個非 Unicode 字形, 用來表示所有在該字體裏不可用的字符 (通常是一個虛線框, 一個 H 的大小, 位於 0x1F 或 0xFFFE). 這使得用戶至少能知道這兒有一個不支持的字符. xterm 用的小的定寬字體比如 6x13 等, 將永遠無法覆蓋所有的 Unicode, 因爲許多文字比如日本漢字只能用比歐洲用戶廣泛使用的大的象素尺寸才能表示. 歐洲使用的典型的 Unicode 字體將只包含大約 1000 到 3000 個字符的子集.

我怎樣才能找出一個 X 字體裏有哪些字形?

X 協議無法讓一個應用程序方便地找出一個 cell-spaced 字體提供哪些字形, 它沒有爲字體提供這樣的量度. 因此 Mark LeisherErik van de Poel (Netscape) 指定了一個新的 _XFREE86_GLYPH_RANGES BDF 屬性, 告訴應用程序該 BDF 字體實現了哪個 Unicode 子集. Mark Leisher 提供了一些樣例代碼以產生並掃描這個屬性, 而 Xmbdfed 3.9 以及更高版本將自動將其加入到由它產生的每個 BDF 文件裏.

與 UTF-8 終端模擬器相關的問題是什麼?

VT100 終端模擬器接受 ISO 2022 (=ECMA-35) ESC 序列, 用於在不同的字符集間切換.

UTF-8 在 ISO 2022 的意義裏是一個 "其他編碼系統" (參考 ECMA 35 的 15.4 節). UTF-8 是在 ISO 2022 SS2/SS3/G0/G1/G2/G3 世界之外的, 因此如果你從 ISO 2022 切換到 UTF-8, 所有的 SS2/SS3/G0/G1/G2/G3 狀態都變得沒有意義了, 直到你離開 UTF-8 並切換回 ISO 2022. UTF-8 是一個沒有國家的編碼, 也就是一個自我終結的短字節序列完全決定了它代表什麼字符, 獨立於任何國家的切換. G0 與 G1 在 ISO 10646 裏與在 ISO 8859-1 裏相同, 而 G2/G3 在 ISO 10646 裏不存在, 因爲任何字符都有固定的位置, 因而不會發聲切換. 在 UTF-8 模式下, 你的終端不會因爲你偶然地裝入一個二進制文件而切換入一種奇怪圖形字符模式. 這使得一個終端在 UTF-8 模式下比在 ISO 2022 模式下要健壯得多, 而且因此可以有辦法將終端鎖在 UTF-8 模式裏, 而不會偶然地回到 ISO 2022 世界裏.

ISO 2022 標準指定了一系列的 ESC % 序列, 以離開 ISO 2022 世界 (指定其他的編碼系統, DOCS), 用於 UTF-8 的許多這樣的序列已經註冊進了 ISO 2375 International Register of Coded Character Sets:

  • ESC %G 從 ISO 2022 裏激活一個未指定實現級別的 UTF-8 模式且允許再返回 ISO 2022.
  • ESC %@ 從 UTF-8 回到 ISO 2022, 條件是通過 ESC %G 進入的 UTF-8
  • ESC %/G 切換進 UTF-8 級別 1 且不返回.
  • ESC %/H 切換進 UTF-8 級別 2 且不返回.
  • ESC %/I 切換進 UTF-8 級別 3 且不返回.

當一個終端模擬器在 UTF-8 模式時, 任何 ISO 2022 逃脫碼序列例如用於切換 G2/G3 等的都被忽略. 一個在 UTF-8 模式下的終端模擬器唯一會執行的 ISO 2022 序列是 ESC %@ 以從 UTF-8 返回 ISO 2022 方案.

UTF-8 仍然允許你使用象 CSI 這樣的 C1 控制字符, 儘管 UTF-8 也使用 0x80-0x9F 範圍裏的字節. 重要的是必須理解在 UTF-8 模式下的終端模擬器必須在執行任何控制字符前對收到的字節流運用 UTF-8 解碼器. C1 字符與其他任何大於 U+007F 的字符一樣需先經過 UTF-8 解碼.

已經有哪些支持 UTF-8 的應用程序了?

  • YuditGaspar Sinai 的自由 X11 Unicode 編輯器
  • Mined 98Thomas Wolff 提供, 是一個可以處理 UTF-8 的文本編輯器.
  • less 版本 346 或更高, 支持 UTF-8
  • C-Kermit 7.0 在傳輸, 終端, 及文件字符集方面支持 UTF-8.
  • Sam 是 Plan9 的 UTF-8 編輯器, 類似於 vi, 也可用於 Linux 和 Win32. (Plan9 是第一個完全轉向 UTF-8, 將其作爲字符編碼的操作系統.)
  • 9termMatty Farrow 提供, 是一個 Plan9 操作系統的 Unicode/UTF-8 終端模擬器的 Unix 移植.
  • Wily 是一個 Plan9 Acme 編輯器的 Unix 實現.
  • ucm-0.1Juliusz Chroboczek 的 Unicode 字符映射表, 一個小工具, 使你可以選中任何一個 Unicode 字符並粘貼進你的應用程序.

有哪些用於改善 UTF-8 支持的補丁?

Postscript 字形的名字與 UCS 代碼是怎麼關聯的?

參考 Adobe 的 Unicode and Glyph Names 指南.

X11 的剪切與粘貼工作在 UTF-8 時是如何完成的?

參考 Juliusz Chroboczek客戶機間 Unicode 文本的交換 草案, 對 ICCCM 的一個擴充的建議, 用一個新的可用於屬性類型(property type)和選中(selection)目標的原子 UTF8_STRING 來處理 UTF-8 的選中.

現在有沒有用於處理 Unicode 的免費的庫?

各種 X widget 對 Unicode 支持的現狀如何?

有什麼關於這個話題的好的郵件列表?

你確實應該訂閱的是 [email protected] 郵件列表, 這是發現標準的作者和其他許多領袖的話語的最好辦法. 訂閱方法是, 用 "subscribe" 作爲標題, "subscribe [email protected] unicode" 作爲正文, 發一條消息到 [email protected].

也有一個專注與改進通常用於 GNU/Linux 系統上應用程序的 UTF-8 支持的郵件列表 [email protected]. 訂閱方法是, 以 "subscribe linux-utf8" 爲內容, 發送消息到 [email protected]. 你也可以瀏覽 linux-utf8 archive

其他相關的還有 XFree86 組的 "字體" 與 "i18n" 列表, 但你必須成爲一名正式的開發者才能訂閱.

更多參考

我不斷地將新的材料加入這份文檔, 因此請定期來查看. 歡迎所有關於改進的建議, 以及自由軟件社區裏關於改善 UTF-8 支持的廣告. UTF-8 用在 Linux 裏是新近的事, 因此我們在將來的幾個月裏可以見到大量的進展.

特別感謝 Ulrich Drepper 和 Bruno Haible 的有價值的註解

Markus Kuhn <<[email protected]>
創建於 1999-06-04 -- 最近更新於 2000-01-15 -- http://www.cl.cam.ac.uk/~mgk25/unicode.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章