Unicode並不只是一個編程工具,它還是一個政治的、經濟的工具。沒有結合世界的語言支持的應用程序通常只能被那些能讀寫ASCII所支持語言的個人使用。這使得建立在ASCII基礎之上的計算機技術脫離了世界上大部分人。Unicode允許程序使用世界上任何一種字符集,因此它支持所有語言。
Unicode讓程序員爲普通人提供用他們本國語言就能使用的軟件。這樣就不用再學一門外語了,而且更容易實現計算機技術社會和財政上的利益。很容易設想,如果用戶必須爲使用因特網瀏覽器而學習烏爾都語的話,您就難以看到計算機在美國的使用。Web就更不會出現了。
Linux承擔了對Unicode很大程度上的支持。Unicode支持被嵌入到內核和代碼開發庫中。在很大程度上,使用程序中幾句簡單的命令就能將它們自動的結合到代碼中。
所有現代字符集的基礎都是在1968年以ANSIX3.4版本出版的美國信息交換標準碼(AmericanStandardCodeforInformationInterchange,ASCII)。一個值得注意的例外是在ASCII之前定義的IBM的擴充的二進制編碼的十進制交換碼(ExtendedBinaryCodedDecimalInformationCode,EBCDIC)。ASCII是一個編碼字符集(codedcharacterset,CCS),換句話說,它是整數到字符表示的映射。ASCII編碼字符集允許用一個八位(基於二進制的,用值0或1表示的)字段或字節(2^8=256)表示256個字符。這是一個高度受限的編碼字符集,它不能表示許多不同語言的所有字符(如中文和日文),不能表示科學符號,更不能表示古代文字(神祕符號和象形文字)和音樂符號。通過更改一個字節的長度而使更大的字符集得以被編碼,這似乎有效但完全不切實際。所有的計算機都基於八位字節。解決方法是一種字符編碼方案(Characterencodingscheme,CES)―用定長或變長的多字節序列能夠表示比256大的數.這些數值接着通過編碼字符集被映射到它們表示的字符。
Unicode通常用作涉及雙字節字符編碼方案的通用術語。UnicodeCCS3.1的官方稱謂是ISO10646-1通用多八字節編碼字符集(UniversalMultipleOctetCodedCharacterSet,UCS)。Unicode3.1版本添加了44,946個新的編碼字符。算上Unicode3.0版本已經存在的49,194個字符,共計94,140個。
Unicode編碼字符集利用了一個由128個三維的組構成的四維編碼空間。其中每個組包含256個二維平面。每個平面由256個一維的行組成,並且每個行有256個單元。每個單元在這個編碼空間內對一個字符編碼,或者被聲明爲未經使用。這種編碼概念被稱爲UCS-4;四個八位元用來表示指定組、平面、行和單元的每個字符。
第一個平面(第00組的第00平面)是基本多語言平面(BasicMultilingualPlane,BMP)。BMP按字母、音節、表意符號和各種符號及數字定義了常規使用的字符。後續的平面用於附加字符或其它還沒有發明的編碼實體。我們需要這完整的範圍去處理世界上的所有語言;特別是擁有將近64,000個字符的一些東亞語言。
BMP被用作雙字節的編碼字符集,這種編碼字符集確定爲ISO10646UCS-2格式。ISO10646UCS-2就是指Unicode(並且兩者相同)。BMP,像所有UCS平面那樣,包含了256行,其中每行包含256個單元,字符僅僅按照BMP中的行和單元的八位元在單元中被編碼。這就允許16位編碼字符能夠被用來書寫大多數商業上最重要的語言。UCS-2不需要代碼頁切換、代碼擴展或代碼狀態。UCS-2是一種將Unicode結合到軟件中的簡單方法,但它只限於支持UnicodeBMP。
若要用8位字節表示一個多於2^8=256個字符的字符編碼系統(charactercodingsystem,CCS),就需要一種字符編碼方案(character-encodingscheme,CES)。
在UNIX中,使用得最多的字符編碼方案是UTF-8。它考慮到了對整個Unicode全部頁和平面的全面支持,而且它仍能正確的識別ASCII。除了UTF-8的其他選擇還有:UCS-4、UTF-16、UTF-7.5、UTF-7、SCSU、HTML和JAVA。
Unicode轉換格式(UnicodeTransformationFormats,UTFs)是一種通過映射多字節編碼中的值來支持Unicode的字符編碼方案。本文將分析最流行的格式―UTF-8字符編碼系統。
UTF-8轉換格式正逐步成爲一種占主導地位的交換國際文本信息的方法,因爲它可以支持世界上所有的語言,而且它還與ASCII兼容。UTF-8使用變長編碼。從0到0x7f(127)的字符把自身編碼成單字節,而將值更大的字符編碼成2到6個字節。
0x00000000-0x0000007F: | 0xxxxxxx | |
0x00000080-0x000007FF: | 110xxxxx10xxxxxx | |
0x00000800-0x0000FFFF: | 1110xxxx10xxxxxx10xxxxxx | |
0x00010000-0x001FFFFF: | 11110xxx10xxxxxx10xxxxxx10xxxxxx | |
0x00200000-0x03FFFFFF: | 111110xx10xxxxxx10xxxxxx10xxxxxx10xxxxxx | |
0x04000000-0x7FFFFFFF: | 1111110x10xxxxxx10xxxxxx10xxxxxx10xxxxxx10xxxxxx |
字節10xxxxxx是一個擴展字節,它的xxxxxx位位置被以二進制表示的字符代碼號的位所填充。這是能夠代表被使用代碼的最短的可能的多字節序列。
Unicode字符版權標記字符0xA9=10101001用UTF-8編碼如下所示:
1100001010101001=0xC20xA9
“不等於”符號字符0x2260=0010001001100000編碼如下所示:
111000101000100110100000=0xE20x890xA0
通過獲取continuationbyte
的值可以看到原始數據:
[1110]0010[10]001001[10]100000
0010001001100000
0010001001100000=0x2260
第一個字節定義後面緊跟的八位元數,如果是7F或更小,這就是等價的ASCII值。每個八位字節以10xxxxxx開頭,確保字節不與ASCII的值混淆。
在Linux平臺上使用UTF-8之前,請確信分發包裏有glibc2.2和XFree864.0或更新的版本。早先的版本缺少UTF-8語言環境支持和ISO10646-1X11字體。
在UTF-8發佈之前,Linux用戶使用各種不同特定語言的擴展ASCII,像歐洲用戶用ISO8859-1或ISO8859-2,希臘用戶使用ISO8859-7,俄羅斯用戶使用KOI-8/ISO8859-5/CP1251(西裏爾字母)。這使得數據交換出現了很多問題,並且需要爲這些編碼之間的差異編寫應用軟件。這種語言支持是不完善的,而且數據交換沒有經過測試。Linux主要的發行商和應用程序開發者正致力於讓主要以UTF-8格式表示的Unicode成爲Linux中的標準。
爲了識別Unicode文件,Microsoft建議所有的Unicode文件應該以ZEROWIDTHNOBREAKSPACE(U+FEFF)字符開頭。這作爲一個“特徵符”或“字節順序標記(byte-ordermark,BOM)”來識別文件中使用的編碼和字節順序。但是,Linux/UNIX並沒有使用BOM,因爲它會破壞現有的ASCII文件的語法約定。在POSIX系統中,選中的語言環境識別了在一個過程中的所有輸入輸出文件期望的編碼形式。
有兩種方法可以將UTF-8支持添加到Linux應用程序中。第一種方法,數據都以UTF-8形式存放在各處,這樣軟件改動很少(被動的)。另一種方法,被讀取的UTF-8數據用標準的C語言庫函數轉變成爲寬字符數組(轉換的)。在輸出時,用函數wcsrtombs()
使字符串被轉變回UTF-8:
#include <wchar.h> size_t wcsrtombs (char *dest, const wchar_t **src, size_t len, mbstate_t *ps); |
方法的選擇取決於應用程序的性質。大多數應用程序可以使用被動的方法操作。這就是在UNIX平臺上使用UTF-8會如此流行的原因。像cat
和echo
那樣的程序就不需要修改。字節流仍只是字節流,並沒有對它進行任何處理。ASCII字符和控制代碼在UTF-8語言環境中不改變。
通過字節計數對字符進行計數的程序需要一些小小的改動。在UTF-8中應用程序不對任何擴展的字節進行計數。如果選擇了UTF-8語言環境,C語言庫的strlen(s)
函數需要用mbstowcs()
函數來代替:
#include <stdlib.h> size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n); |
strlen
的一種常見用法是估算顯示寬度。中文和其它表意符號將佔用兩列位置。wcwidth()
函數用來測試每個字符的顯示寬度:
#include < wchar.h> int wcwidth(wchar_t wc); |
在正式情況下,從GNUglibc2.2開始,wchar_t類型只爲32位的ISO10646格式數值所特定使用,與當前使用的語言環境無關。通過ISOC99所要求的__STDC_ISO_10646__宏的定義作爲信號通知應用程序。__STDC_ISO_10646__的定義用來指出wchar_t是Unicode。精確的值是一個十進制的yyyymmL格式的常數。例如,使用:
#define __STDC_ISO_10646__ 200104L |
是爲指出wchar_t類型的值是由ISO/IEC10646和到指定的年月爲止的所有修正與技術勘誤定義的字符編碼表示。
對wchar_t的利用如這個示例所示,使用宏確定在ISOC99可移植代碼中寫雙引號的方法。
#if __STDC_ISO_10646__ printf("%lc", 0x201c); #else putchar('"'); #fi |
激活UTF-8的恰當的辦法是POSIX語言環境機制。語言環境是一種包含有關軟件行爲特定文化約定的配置設定。它包含了字符編碼、日期/時間符號、分類規則以及度量系統。語言環境的名稱通常由ISO639-1語言、ISO3166-1國家或地區代碼以及可選的編碼名稱和其它限定符組成。您可以用命令locale-a
獲取所有安裝在系統上的語言環境列表(通常在/usr/lib/locale/)。
如果沒有預安裝UTF-8語言環境,你可以用localedef
命令生成它。若要爲某個特定用戶生成並激活一個德語的UTF-8語言環境,請使用如下語句:
localedef -v -c -i de_DE -f UTF-8 $HOME/local/locale/de_DE.UTF-8 export LOCPATH=$HOME/local/locale export LANG=de_DE.UTF-8 |
有時候爲所有用戶添加UTF-8語言環境會很有用。root用戶使用如下指令就可以完成:
localedef -v -c -i de_DE -f UTF-8 /usr/share/locale/de_DE.UTF-8 |
若要爲每個用戶將這個語言環境設爲缺省值,可以將以下行添加到/etc/profile文件中:
export LANG=de_DE.UTF-8 |
處理多字節字符代碼序列的函數行爲依賴於當前語言環境的LC_CTYPE類別;它確定了依賴語言環境的多字節編碼。值LANG=de_DE(德語)會導致輸出按ISO8859-1被格式化。值LANG=de_DE.UTF-8會把輸出格式化成UTF-8。語言環境設置會導致printf
中的%ls
格式說明符調用wcsrtombs()
函數以便於將寬字符的參數字符串轉換成依賴語言環境的多字節編碼。語言環境中的國家或地區標識符如:LC_CTYPE=en_GB(英國英語)和LC_CTYPE=en_AU(澳大利亞英語),它們之間的差異只在LC_MONETARY類別中,原因在於貨幣的名稱和打印貨幣數量的規則不同。
請給您首選的語言環境設置環境變量LANG。當一個C程序執行setlocale()
函數時:
#include <stdio.h> #include <locale.h> //char *setlocale(int category, const char *locale); int main() { if (!setlocale(LC_CTYPE, "")) { fprintf(stderr, "Locale not specified. Check LANG, LC_CTYPE, LC_ALL. "); return 1; } |
C語言庫將會依次測試環境變量LC_ALL、LC_CTYPE和LANG。其中第一個含值的環境變量將決定爲LC_CTYPE類別裝入哪種語言環境數據。語言環境數據分裂成獨立的類別。值LC_CTYPE定義了字符編碼,而LC_COLLATE定義了排序順序。我們用LANG環境變量爲所有類別設置缺省語言環境,但LC_*變量可以用來覆蓋單個類別。
您可以用命令localecharmap
查詢當前語言環境中字符編碼的名稱。如果您從LC_CTYPE類別中成功選取了UTF-8語言環境,會輸出UTF-8。命令locale-m
提供一張已安裝的所有字符編碼名稱的列表。
如果您使用專門的C語言庫的多字節函數來完成所有外部字符編碼和內部使用的wchar_t編碼之間的轉換,那麼C語言庫將承擔責任,根據LC_CTYPE使用正確的編碼方式。這甚至不需要程序被明確的編碼成當前的多字節編碼。
如果需要一個應用程序能明確的支持UTF-8(或其它編碼)轉換方法而不用libc多字節函數,則應用程序必須確定是否需要激活UTF-8模式。帶有<langinfo.h>庫頭文件的與X/Open兼容系統可以用如下代碼:
BOOL utf8_mode = FALSE; if( ! strcmp(nl_langinfo(CODESET), "UTF-8") utf8_mode = TRUE; |
爲檢測當前語言環境是否使用了UTF-8編碼。首先必須調用setlocale(LC_CTYPE,"")
函數,依據環境變量設置語言環境。nl_langinfo(CODESET)函數也是由localecharmap
命令調用,從而查找當前語言環境指定的編碼名稱。
另一種可以使用的方法是查詢語言環境變量:
char *s; BOOL utf8_mode = FALSE; if ((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) || (s = getenv ("LANG"))) { if (strstr(s, "UTF-8")) utf8_mode = TRUE; } |
這項測試假設UTF-8語言環境名稱中有值“UTF-8”,但實際情況並不總是如此,所以應該使用nl_langinfo()
方法。
爲支持世界上的所有語言,需要一種具有八位字節字符編碼策略的字符編碼系統,它的字符應多於ASCII(一種使用無符號字節的擴展版本)的2^8=256個字符。Unicode就是這樣一種字符編碼系統,它具有由128個三維組(帶有由大量字符編碼方案的方法支持的94,140個定義好的字符值)組成的四維編碼空間,在Linux中更流行的字符編碼方案是Unicode轉換格式UTF-8。
您可以參閱本文在developerWorks全球站點上的英文原文.
請訪問Unicode聯盟的Unicode主頁,這裏定義了Unicode字符之間的行爲和關係,併爲實現者提供了技術信息。
國際標準組織(InternationalOrganizationforStandardization,ISO)是一個由140個國家組成的全球性的國家標準社團聯盟。
ANSI是個私有的、非營利組織,它管理並調整U.S.的志願標準化以及一致性評價系統。
ISOC99Draft(AcrobatPDF格式,556頁),是新的C語言標準,來自Calgary大學Ben的C編程課程。
請閱讀RomanCzyborra的Unix環境下的Unicode。
請閱讀IANA(InternetAssignedNumbersAuthority)中的IANACharsetRegistrationProcedures。
請參閱Virginia大學圖書館RobertsonMedia中心的UnicodeMusicSymbols。
請看看graphicrepresentationoftheRoadmaptotheBMP,Plane0oftheUCS。這些表包含了由0號,也就是通用字符集(UniversalCharacterSet,UCS)的基本多語言平面(BasicMultilingualPlane,BMP)實際大小的映射組成的。EversonGunnTeoranta是一個自1990年開辦的支持少數民族語言團體的軟件和出版公司,由MichaelEverson和MarionGunn共同建立。
請瀏覽UTF-8andUnicodeFAQforUNIX/Linux,MarkusKuhn的綜合性的one-stop信息資源,關於您如何在POSIX系統(Linux,UNIX)使用Unicode/UTF-8。
請檢查TechnologyAppraisalsLtd的SolutionGivenbytheUniversalCharacterSet,其中提供了獨立的、高質量的有關電子商務系統、電子信息傳遞、XML、網絡和IT安全的信息、教育和培訓。
請閱讀MulberryTechnologies,Inc的Unicodepresentationtitled“10646andAllThat”,一個專攻基於SGML和XML系統的電子出版物的諮詢公司。
請諮詢Linux程序員手冊上的UTF-8―anASCIIcompatiblemulti-byteUnicodeencoding。
請閱讀UnicodeStandardAnnex#15UnicodeNormalizationForms,一篇描寫了四種Unicode文本標準化格式規範的文檔。有了這些格式,等價的(規範或是兼容的)文本將會有同樣的二進制表式。當實現工具在標準化的格式中保留了一個字符串,可以確保有一個以二進制形式表現的獨一無二的等價字符串。
請閱讀man-pages.net上的
mbstowcs
,它把多字節字符串轉換成了寬字符的字符串,man-pages.net爲Linux手冊頁面提供了永久的基於Web的歸檔文件。請閱讀HewlettPackard的開發者資源站點的Linux程序員手冊上的
wcsrtombs
,它能將寬字符的字符串轉化爲多字節字符串。請閱讀MKS工具箱文檔中的
setlocale()
,它能改變或查詢語言環境。MKS軟件公司是在Windows環境或混合UNIX/Linux和Windows環境中用於系統管理和開發的Windows自動化工具的領先供應商。請學習IBMClassesforUnicode(ICU),一個C語言和C++語言庫,它在許多平臺上提供了健壯的和功能完善的Unicode支持。
請參閱IBM的“IntroductiontoUnicode”站點,這裏深入涵蓋了Unicode基礎知識。
在IBM的關於新興技術的alphaWorks站點。請參閱:
UnicodeCompressor,這裏提供了使用標準Unicode壓縮方案的壓縮和解壓縮Unicode文本的工具
UnicodeNormalizer,爲實現快速排序和搜索將Java字符串對象轉換爲標準Unicode格式。
請閱讀TWBurger撰寫的“CyrillicinUnicode”和JimMelnick撰寫的“MultilingualformsinUnicode”,也在developerWorks上。
請在developerWorks上瀏覽更多Linux參考資料。
FROM:http://www.ibm.com/developerworks/cn/linux/i18n/unicode/linuni/