1. 程式人生 > >struts原理與實踐(四)

struts原理與實踐(四)

本篇我們來討論一下struts的國際化程式設計問題,即所謂的i18n程式設計問題,這一篇我們討論其基礎部分。與這個問題緊密相關的是在各java論壇中被頻繁提及的中文亂碼問題,因為,英、美程式設計人員較少涉及到中文亂碼問題,因此,這方面的英文資料也是非常奇缺的,同時也很少找到這方面比較完整的中文資料,本文也嘗試對中文亂碼問題做一些探討。要解決上述問題,需要有一定的字符集方面的知識,下面,我們就先介紹字符集的有關情況:

一、從ASCII到Unicode(UTF-8)

電子計算機技術是從美國開始發展起來的,因為美國使用的文字為英文,美國規定的計算機資訊交換用的字元編碼集是人們熟知的擴充套件ASCII碼,它以8bit位元組為單位儲存,ASCII的0-31及127為控制符,32-126為可見字元,包括所有的英文字母,阿拉伯數字和其他一些常見符號,128-255的ASCII碼則沒有定義。

ASCII對英語國家是夠用了,但對其他西歐國家卻不夠用,因此,人們將ASCII擴充套件到0-255的範圍,形成了ISO-8859-1字符集。值得一提的是,因為考慮到程式中處理的資訊大多是西文資訊,因此有些WEB容器(如:Tomcat4.x)在處理所接收到的request字串時,如果您沒指定request的編碼方式則系統就預設地採用ISO-8859-1,明白這一點對理解後面的問題會有幫助。

相比西方的拼音文字,東方的文字(如中文)的字元數要大得多,根本不可能在一個位元組內將它們表示出來,因此,它們以兩個位元組為單位儲存,以中文國標字符集GB2312為例,它的第一個位元組為128-255。系統可以據此判斷,若第一個位元組大於127,則把與該位元組後緊接著的一個位元組結合起來共兩個位元組組成一箇中文字元。這種由多個位元組儲存一個字元的字符集叫多位元組字符集(MultiByte Charsets),對應的象ASCII這種用一個位元組儲存一個字元的字符集叫單位元組字符集(SingleByte Charsets)。在GB2312字符集中,ASCII字元仍然用一個位元組儲存,換句話說該ASCII是該字符集的子集。

GB2312只包含數千個常用漢字,往往不能滿足實際需要,因此,人們對它進行擴充套件,這就有了我們現在廣泛使用的GBK字符集,GBK是現階段Windows及其他一些中文作業系統的預設字符集。它包含2萬多個字元,除了保持和GB2312相容外,還包含繁體中文字,日文字元和朝鮮字元。值得注意的是GBK只是一個規範而不是國家標準,新的國家標準是GB18030-2000,它是比GBK包含字元更多的字符集。

我國的臺灣地區使用的文字是繁體字,其字符集是BIG5,而日本採用的字符集則是SJIS。它們的編碼方法與GB2312類似,它們的ASCII字元部分是相容的,但擴充套件部分的編碼則是不相容的,比如這幾種字符集中都有"中文"這兩個字元,但他們在各自的字符集中的編碼並不相同,這就是用GB2312寫成的網頁用BIG5瀏覽時,看到的是亂糟糟的資訊的原因。

可見,在字符集的世界裡,呈現給我們的是一個群雄割據的局面,各字符集擁有一塊自己的地盤。這給各國和各地區交換資訊帶來了很大的困難,同時,也給國際化(本地化)程式設計造成了很大的麻煩。

常言道:"分久必合",隨著國際標準ISO10646定義的通用字符集(Universal Character Set即UCS)的出現,使這種局面發生了徹底的改觀。UCS 是所有其他字符集標準的一個超集. 它保證與其他字符集是雙向相容的. 就是說, 如果你將任何文字字串翻譯到 UCS格式, 然後再翻譯回原編碼, 你不會丟失任何資訊。UCS 包含了用於表達所有已知語言的字元。不僅包括拉丁語、希臘語、 斯拉夫語、希伯來語、阿拉伯語、亞美尼亞語和喬治亞語的描述、還包括中文、 日文和韓文這樣的象形文字、 以及平假名、片假名、 孟加拉語、 旁遮普語果魯穆奇字元(Gurmukhi)、 泰米爾語、印.埃納德語(Kannada)、Malayalam、泰國語、 寮國語、 漢語拼音(Bopomofo)、Hangul、 Devangari、Gujarati、Oriya、Telugu 以及其他數也數不清的語。對於還沒有加入的語言, 由於正在研究怎樣在計算機中最好地編碼它們, 因而最終它們都將被加入。

ISO 10646 定義了一個 31 位的字符集。 然而, 在這巨大的編碼空間中, 迄今為止只分配了前 65534 個碼位 (0x0000 到 0xFFFD)。 這個 UCS 的 16位子集稱為 基本多語言面 (Basic Multilingual Plane, BMP)。 將被編碼在 16 位 BMP 以外的字元都屬於非常特殊的字元(比如象形文字), 且只有專家在歷史和科學領域裡才會用到它們。

UCS 不僅給每個字元分配一個程式碼, 而且賦予了一個正式的名字。 表示一個 UCS 值的十六進位制數, 通常在前面加上 "U+", 就象 U+0041 代表字元"拉丁大寫字母A"。 UCS 字元 U+0000 到 U+007F 與 US-ASCII(ISO 646) 是一致的, U+0000 到 U+00FF 與 ISO 8859-1(Latin-1) 也是一致的。這裡要注意的是它是以16bit為單位儲存,即便對字母"A"也是用16bit,這是與前面介紹的所有字符集不同的地方。

歷史上,在國際標準化組織研究ISO10646標準的同時,另一個由多語言軟體製造商組成的協會也在從事創立單一字符集的工作,這就是現在人們熟知的Unicode。幸運的是,1991年前後ISO10646和Unicode的參與者都認識到,世界上不需要兩個不同的單一字符集。他們合併雙方的工作成果,併為創立單一編碼表而協同工作。兩個專案仍都存在並獨立地公佈各自的標準,都同意保持ISO10646和Unicode的碼錶相容,並緊密地共同調整任何未來的擴充套件。這與當年在PC機上的作業系統MS-dos與PC-dos的情形有些相象。後面,我們將視ISO10646和Unicode為同一個東西。

有了Unicode,字符集問題接近了完美的解決,但不要高興得過早。由於歷史的原因:一些作業系統如:Unix、Linux等都是基於ASCII設計的。此外,還有一些資料庫管理系統軟體如:Oracle等也是圍繞ASCII來設計的(從其8i的白皮書上介紹的設定系統字符集和欄位的字符集中可以間接地看到這一點)。在這些系統中直接用Unicode會導致嚴重的問題。用這些編碼的字串會包含一些特殊的字元, 比如 '\0' 或 '/', 它們在 檔名和其他 C 庫函式引數裡都有特別的含義。 另外, 大多數使用 ASCII 檔案的 UNIX 下的工具, 如果不進行重大修改是無法讀取 16 位的字元的。 基於這些原因, 在檔名, 文字檔案, 環境變數等地方,直接使用Unicode是不合適的。

在 ISO 10646-1 Annex R 和 RFC 2279 裡定義的 UTF-8 (Unicode Transformation Form 8-bit form)編碼沒有這些問題。

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 範圍裡。 這使得重新同步非常容易, 並使編碼無國界,且很少受丟失位元組的影響。