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 R 和 RFC 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-8 或 de_DE.ISO_8859-1) 中選擇的 locale 所指定的編碼來打印這段文字. 你的編譯器必須運行在與該 C 源文件所用編碼相應的 locale 中, 在目標文件中以上的寬字符串將改為 wchar_t 字符串存儲. 在輸出時, 運行時庫將把 wchar_t 字符串轉換回與程序執行時的 locale 相應的編碼.
注意, 類似這樣的操作:
char c = L"a";
只允許從 U+0000 到 U+007F (7 位 ASCII) 范圍里的字符. 對于非 ASCII 字符, 不能直接從 wchar_t 到 char 轉換.
現在, 象 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-48 和 Unicode 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 Leisher 和 Erik 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 的應用程序了?
- Yudit 是 Gaspar Sinai 的自由 X11 Unicode 編輯器
- Mined 98 由 Thomas Wolff 提供, 是一個可以處理 UTF-8 的文本編輯器.
- less 版本 346 或更高, 支持 UTF-8
- C-Kermit 7.0 在傳輸, 終端, 及文件字符集方面支持 UTF-8.
- Sam 是 Plan9 的 UTF-8 編輯器, 類似于 vi, 也可用于 Linux 和 Win32. (Plan9 是第一個完全轉向 UTF-8, 將其作為字符編碼的操作系統.)
- 9term 由 Matty Farrow 提供, 是一個 Plan9 操作系統的 Unicode/UTF-8 終端模擬器的 Unix 移植.
- Wily 是一個 Plan9 Acme 編輯器的 Unix 實現.
- ucm-0.1 是 Juliusz Chroboczek 的 Unicode 字符映射表, 一個小工具, 使你可以選中任何一個 Unicode 字符并粘貼進你的應用程序.
有哪些用于改善 UTF-8 支持的補丁?
- Robert Brady 提供了一個 patch for less 340 (現在已經合并進了 less 344)
- Bruno Haible 提供了用于 stty, Linux 核心 tty 等的 多個補丁.
- Otfried Cheong 編寫了 Unicode encoding for GNU Emacs 工具箱, 使 Mule 能夠處理 UTF-8 文件.
Postscript 字形的名字與 UCS 代碼是怎么關聯的?
參考 Adobe 的 Unicode and Glyph Names 指南.
X11 的剪切與粘貼工作在 UTF-8 時是如何完成的?
參考 Juliusz Chroboczek 的 客戶機間 Unicode 文本的交換 草案, 對 ICCCM 的一個擴充的建議, 用一個新的可用于屬性類型(property type)和選中(selection)目標的原子 UTF8_STRING 來處理 UTF-8 的選中.
現在有沒有用于處理 Unicode 的免費的庫?
- IBM Classes for Unicode
- Mark Leisher 的 UCData Unicode 字符屬性庫和 wchar_t 支持測試碼.
各種 X widget 對 Unicode 支持的現狀如何?
- GScript - Unicode 與復雜文本處理 是一個為 GTK+ 增加全功能的 Unicode 支持的項目.
- Qt 2.0 現在支持使用 *-ISO10646-1 字體了.
- FriBidi 是 Dov Grobgeld 的 Unicode 雙向算法的免費實現.
有什么關于這個話題的好的郵件列表?
你確實應該訂閱的是 unicode@unicode.org 郵件列表, 這是發現標准的作者和其他許多領袖的話語的最好辦法. 訂閱方法是, 用 "subscribe" 作為標題, "subscribe YOUR@EMAIL.ADDRESS unicode" 作為正文, 發一條消息到 unicode-request@unicode.org.
也有一個專注與改進通常用于 GNU/Linux 系統上應用程序的 UTF-8 支持的郵件列表 linux-utf8@nl.linux.org. 訂閱方法是, 以 "subscribe linux-utf8" 為內容, 發送消息到 majordomo@nl.linux.org. 你也可以瀏覽 linux-utf8 archive
其他相關的還有 XFree86 組的 "字體" 與 "i18n" 列表, 但你必須成為一名正式的開發者才能訂閱.
更多參考
- Bruno Haible 's Unicode HOWTO.
- The Unicode Standard, Version 2.0
- Unicode Technical Reports
- Mark Davis' Unicode FAQ
- ISO/IEC 10646-1:1993
- Frank Tang's I?t?rnati?nàliz?ti?n Secrets
- Unicode Support in the Solaris 7 Operating Environment
- The USENIX paper by Rob Pike and Ken Thompson on the introduction of UTF-8 under Plan9 reports about the first operating system that migrated already in 1992 completely to UTF-8 (which was at the time still called UTF-2).
- Li18nux is a project initiated by several Linux distributors to enhance Unicode support for Linux.
- The Online Single Unix Specification contains definitions of all the ISO C Amendment 1 function, plus extensions such as wcwidth().
- The Open Group's summary of ISO C Amendment 1.
- GNU libc
- The Linux Console Tools
- The Unicode Consortium character database and character set conversion tables are an essential resource for anyone developping Unicode related tools.
- Other conversion tables are available from Microsoft and Keld Simonsen's WG15 archive.
- Michael Everson's ISO10646-1 archive contains online versions of many of the more recent ISO 10646-1 amendments, plus many other goodies. See also his Roadmaps to the Universal Character Set.
- An introduction into The Universal Character Set (UCS).
- Otfried Cheong's essey on Han Unification in Unicode
- The AMS STIX project is working on revising and extending the mathematical characters for Unicode 4.0 and ISO 10646-2.
- Jukka Korpela's Soft hyphen (SHY) - a hard problem? is an excellent discussion of the controversy surrounding U+00AD.
- James Briggs' Perl, Unicode and I18N FAQ.
我不斷地將新的材料加入這份文檔, 因此請定期來查看. 歡迎所有關于改進的建議, 以及自由軟件社區里關于改善 UTF-8 支持的廣告. UTF-8 用在 Linux 里是新近的事, 因此我們在將來的几個月里可以見到大量的進展.
特別感謝 Ulrich Drepper 和 Bruno Haible 的有價值的注解
Markus Kuhn <<Markus.Kuhn@cl.cam.ac.uk>
創建于 1999-06-04 -- 最近更新于 2000-01-15 -- http://www.cl.cam.ac.uk/~mgk25/unicode.html