關於一篇中文編碼的文章,挺不錯的,就是沒有分享外鏈。html
下面的文章內容只是簡單的複製/粘貼,若是以爲不錯的話,能夠查看原文。node
原文地址:http://www.searchtb.com/2012/04/chinese_encode.htmlpython
PS:使用fedora系統,剛剛輸入「粘貼」時,竟然要用拼音「niantie」才能出來!!!linux
==================================================================web
編碼問題的例子shell
在windows自帶的notepad(記事本)程序中輸入「聯通」兩個字,保存後再次打開,會發現「聯通」不見了,代之以「��ͨ」的亂碼。這是windows平臺上典型的中文編碼問題。即文件保存的時候是按照ANSI編碼(其實就是GB2312,後面會詳細介紹)保存,打開的時候程序按照UTF-8方式對內容解釋,因而就出現了亂碼。避免亂碼的方式很簡單,在「文件」菜單中選擇「打開」命令,選擇保存的文件,而後選擇「ANSI」編碼,此時就能看到久違的「聯通」兩個字了。vim
在Linux平臺上若是使用cat等命令查看文件中的中文內容時,可能出現亂碼。這也是編碼的問題。簡單的說是文件時按照A編碼保存,可是cat命令按照當前Locale設定的B編碼去查看,在B和A不兼容的時候就出現了亂碼。windows
爲何寫這篇文章數組
中文編碼因爲歷史緣由牽扯到很多標準,在不瞭解的時候感受一頭霧水;但其實理解編碼問題並不須要你深刻了解各個編碼標準,只要你明白了前因後果,瞭解了關鍵的知識點,就能分析和解決平常開發工做中碰到的大部分編碼問題。有感於我看過的資料和文章要麼不夠全面,要麼略顯枯燥,因此經過這篇文章記錄下筆者在平常工做中碰到的中文編碼原理相關問題,目的主要是自我總結,若是能給讀者提供一些幫助那就算是意外之喜了。因爲嚴謹的編碼標準對我來講是無趣的,枯燥的,難以記憶的,本文嘗試用淺顯易懂的生活語言解釋中文編碼相關的(也可能不相關的)一些問題,這也是爲何取名雜談的緣由。本文確定存在不規範不全面的地方,我會在參考資料裏給出官方文檔的連接,也歡迎讀者在評論中提出更好的表達方式&指出錯誤,不勝感激。瀏覽器
對編碼問題的理解我認爲分爲三個層次,第一個層次:概念,知道各個編碼標準的應用場景,瞭解之間的差別,能分析和解決常見的一些編碼問題。第二個層次:標準,掌握編碼的細節,如編碼範圍,編碼轉換規則,知道這些就能自行開發編碼轉換工具。第三個層次,使用,瞭解中文的編碼2進制存儲,在程序開發過程當中選擇合理的編碼並處理中文。爲了不讓讀者陷入編碼標準的黑洞沒法脫身(不相信?看看unicode的規範就明白個人意思了),同時因爲編碼查詢&轉換工具等都有現成工具可使用,本文只涉及第一個層次,不涉及第二層次,在第三層次上會作一些嘗試。在本文的最後提供了相關連接供對標準細節感興趣的同窗繼續學習。最後,本文不涉及具體軟件的亂碼問題解決,如ssh,shell,vim,screen等,這些話題留給劍豪同窗專文闡述。
一切都是由於電腦不識字
電腦很聰明,能夠幫咱們作不少事情,最開始主要是科學計算,這也是爲何電腦別名計算機。電腦又很笨,在她的腦子裏只有數字,即全部的數據在存儲和運算時都要使用二進制數表示。這在最初電腦主要用來處理大量複雜的科學計算時不是什麼大問題可是當電腦逐步走入普通人的生活時,狀況開始變遭了。辦公自動化等領域最主要的需求就是文字處理,電腦如何來表示文字呢?這個問題固然難不倒聰明的計算機科學家們,用數字來表明字符唄。這就是「編碼」。
英文的終極解決方案:ASCII
每一個人均可以約定本身的一套編碼,只要使用方之間瞭解就ok了。好比說咱倆約定0×10表示a,0×11表示b。在一開始也的確是這樣的,出現了各式各樣的編碼。這樣有兩個問題:1.各個編碼的字符集不同,有的多,有的少。2.相同字符的編碼也不同。你這裏a是0×10.他那裏a多是0×30。因而你保存的文件他就不能直接用,必需要轉換編碼。隨着溝通範圍的擴大,採用不一樣編碼的人們互相通訊就亂套了,這就是咱們常說的:雞同鴨講。若是要避免這種混亂,那麼你們就必須使用相同的編碼規則,因而美國有關的標準化組織就出臺了ASCII(American Standard Code for Information Interchange)編碼,統一規定了英文經常使用符號用哪些二進制數來表示。ASCII是標準的單字節字符編碼方案,用於基於文本的數據。
ASCII最初是美國國家標準,供不一樣計算機在相互通訊時用做共同遵照的西文字符編碼標準,已被國際標準化組織(International Organization for Standardization, ISO)定爲國際標準,稱爲ISO 646標準。適用於全部拉丁文字字母。ASCII 碼使用指定的7 位或8 位二進制數組合來表示128 或256 種可能的字符。標準ASCII 碼也叫基礎ASCII碼,使用7 位二進制數來表示全部的大寫和小寫字母,數字0 到九、標點符號, 以及在美式英語中使用的特殊控制字符。而最高位爲1的另128個字符(80H—FFH)被稱爲「擴展ASCII」,通常用來存放英文的製表符、部分音標字符等等的一些其它符號。
其中:0~31及127(共33個)是控制字符或通訊專用字符(其他爲可顯示字符),32~126(共95個)是字符(32是空格),其中48~57爲0到9十個阿拉伯數字,65~90爲26個大寫英文字母,97~122號爲26個小寫英文字母,其他爲一些標點符號、運算符號等。
ascii 編碼表
如今全部使用英文的電腦終於能夠用同一種編碼來交流了。理解了ASCII編碼,其餘字母型的語言編碼方案就舉一反三了。
一波三折的中文編碼
第一次嘗試:GB2312
ASCII這種字符編碼規則顯然用來處理英文沒有什麼問題,它的出現極大的促進了信息在西方尤爲是美國的傳播和交流。可是對於中文,經常使用漢字就有6000以上,ASCII 單字節編碼顯然是不夠用。爲了粉碎美帝國主義經過編碼限制中國人民使用電腦的無恥陰謀,中國國家標準總局發佈了GB2312碼即中華人民共和國國家漢字信息交換用編碼,全稱《信息交換用漢字編碼字符集——基本集》,1981年5月1日實施,通行於大陸。GB2312字符集中除經常使用簡體漢字字符外還包括希臘字母、日文平假名及片假名字母、俄語西裏爾字母等字符,未收錄繁體中文漢字和一些生僻字。 EUC-CN能夠理解爲GB2312的別名,和GB2312徹底相同。
GB2312是基於區位碼設計的,在區位碼的區號和位號上分別加上A0H就獲得了GB2312編碼。這裏第一次提到了「區位碼」,我就連帶把下面這幾個讓人摸不到頭腦的XX碼一鍋端了吧:
區位碼,國標碼,交換碼,內碼,外碼
區位碼:就是把中文經常使用的符號,數字,漢字等分門別類進行編碼。區位碼把編碼表分爲94個區,每一個區對應94個位,每一個位置就放一個字符(漢字,符號,數字都屬於字符)。這樣每一個字符的區號和位號組合起來就成爲該漢字的區位碼。區位碼通常用10進制數來表示,如4907就表示49區7位,對應的字符是「學」。區位碼中01-09區是符號、數字區,16-87區是漢字區,10-15和88-94是未定義的空白區。它將收錄的漢字分紅兩級:第一級是經常使用漢字計3755個,置於16-55區,按漢語拼音字母/筆形順序排列;第二級漢字是次經常使用漢字計3008個,置於56-87區,按部首/筆畫順序排列。在網上搜索「區位碼查詢系統」能夠很方便的找到漢字和對應區位碼轉換的工具。爲了不廣告嫌疑和死鏈,這裏就不舉例了。
國標碼: 區位碼沒法用於漢字通訊,由於它可能與通訊使用的控制碼(00H~1FH)(即0~31,還記得ASCII碼特殊字符的範圍嗎?)發生衝突。因而ISO2022規定每一個漢字的區號和位號必須分別加上32(即二進制數00100000,16進制20H),獲得對應的國標交換碼,簡稱國標碼,交換碼,所以,「學」字的國標交換碼計算爲:
1
2
3
4
00110001 00000111
+ 00100000 00100000
-------------------
01010001 00100111
用十六進制數表示即爲5127H。
交換碼:即國標交換碼的簡稱,等同上面說的國標碼。
內碼:因爲文本中一般混合使用漢字和西文字符,漢字信息若是不予以特別標識,就會與單字節的ASCII碼混淆。此問題的解決方法之一是將一個漢字當作是兩個擴展ASCII碼,使表示GB2312漢字的兩個字節的最高位都爲1。即國標碼加上128(即二進制數10000000,16進制80H)這種高位爲1的雙字節漢字編碼即爲GB2312漢字的機內碼,簡稱爲內碼。20H+80H=A0H。這也就是常說的在區位碼的區號和位號上分別加上A0H就獲得了GB2312編碼的由來。
1
2
3
4
00110001 00000111
+ 10100000 10100000
-------------------
11010001 10100111
用十六進制數表示即爲D1A7H。
外碼:機外碼的簡稱,就是漢字輸入碼,是爲了經過鍵盤字符把漢字輸入計算機而設計的一種編碼。 英文輸入時,相輸入什麼字符便按什麼鍵,外碼和內碼一致。漢字輸入時,可能要按幾個鍵才能輸入一個漢字。 漢字輸入方案有成百上千個,可是這千差萬別的外碼輸入進計算機後都會轉換成統一的內碼。
最後總結一下上面的概念。中國國家標準總局把中文經常使用字符編碼爲94個區,每一個區對應94個位,每一個字符的區號和位號組合起來就是該字符的區位碼, 區位碼用10進制數來表示,如4907就表示49區7位,對應的字符是「學」。 因爲區位碼的取值範圍與通訊使用的控制碼(00H~1FH)(即0~31)發生衝突。每一個漢字的區號和位號分別加上32(即16進制20H)獲得國標碼,交換碼。「學」的國標碼爲5127H。因爲文本中一般混合使用漢字和西文字符,爲了讓漢字信息不會與單字節的ASCII碼混淆,將一個漢字當作是兩個擴展ASCII碼,即漢字的兩個字節的最高位置爲1,獲得的編碼爲GB2312漢字的內碼。「學」的內碼爲D1A7H。不管你使用什麼輸入法,經過什麼樣的按鍵組合把「學」輸入計算機,「學」在使用GB2312(以及兼容GB2312)編碼的計算機裏的內碼都是D1A7H。
第二次嘗試:GBK
GB2312的出現基本知足了漢字的計算機處理須要,但因爲上面提到未收錄繁體字和生僻字,從而不能處理人名、古漢語等方面出現的罕用字,這致使了1995年《漢字編碼擴展規範》(GBK)的出現。GBK編碼是GB2312編碼的超集,向下徹底兼容GB2312,兼容的含義是不只字符兼容,並且相同字符的編碼也相同,同時在字彙一級支持ISO/IEC10646—1和GB 13000—1的所有中、日、韓(CJK)漢字,共計20902字。GBK還收錄了GB2312不包含的漢字部首符號、豎排標點符號等字符。CP936和GBK的有些許差異,絕大多數狀況下能夠把CP936看成GBK的別名。
第三次嘗試:GB18030
GB18030編碼向下兼容GBK和GB2312。GB18030收錄了全部Unicode3.1中的字符,包括中國少數民族字符,GBK不支持的韓文字符等等,也能夠說是世界大多民族的文字符號都被收錄在內。GBK和GB2312都是雙字節等寬編碼,若是算上和ASCII兼容所支持的單字節,也能夠理解爲是單字節和雙字節混合的變長編碼。GB18030編碼是變長編碼,有單字節、雙字節和四字節三種方式。
其實,這三個標準並不須要死記硬背,只須要了解是根據應用需求不斷擴展編碼範圍便可。從GB2312到GBK再到GB18030收錄的字符愈來愈多便可。萬幸的是一直是向下兼容的,也就是說一個漢字在這三個編碼標準裏的編碼是如出一轍的。這些編碼的共性是變長編碼,單字節ASCII兼容,對其餘字符GB2312和GBK都使用雙字節等寬編碼,只有GB18030還有四字節編碼的方式。這些編碼最大的問題是2個。1.因爲低字節的編碼範圍和ASCII有重合,因此不能根據一個字節的內容判斷是中文的一部分仍是一個獨立的英文字符。2.若是有兩個漢字編碼爲A1A2B1B2,存在A2B1也是一個有效漢字編碼的特殊狀況。這樣就不能直接使用標準的字符串匹配函數來判斷一個字符串裏是否包含某一個漢字,而須要先判斷字符邊界而後才能進行字符匹配判斷。
最後,提一個小插曲,上面講的都是大陸推行的漢字編碼標準,使用繁體的中文社羣中最經常使用的電腦漢字字符集標準叫大五碼(Big5),共收錄13,060箇中文字,其中有二字爲重覆編碼(實在是不該該)。Big5雖普及於中國的臺灣、香港與澳門等繁體中文通行區,但長期以來並不是當地的國家標準,而只是業界標準。倚天中文系統、Windows等主要系統的字符集都是以Big5爲基準,但廠商又各自增刪,衍生成多種不一樣版本。2003年,Big5被收錄到臺灣官方標準的附錄當中,取得了較正式的地位。這個最新版本被稱爲Big5-2003。
天下歸一Unicode
看了上面的多箇中文編碼是否是有點頭暈了呢?若是把這個問題放到全世界n多個國家n多語種呢?各國和各地區本身的文字編碼規則互相沖突的狀況全球信息交換帶來了很大的麻煩。
要真正完全解決這個問題,上面介紹的那些經過擴展ASCII修修補補的方式已經走不通了,而必須有一個全新的編碼系統,這個系統要能夠將中文、日文、法文、德文……等等全部的文字統一塊兒來考慮,爲每個文字都分配一個單獨的編碼。因而,Unicode誕生了。Unicode(統一碼、萬國碼、單一碼)爲地球上(之後會包括火星,金星,喵星等)每種語言中的每一個字符設定了統一而且惟一的二進制編碼,以知足跨語言、跨平臺進行文本轉換、處理的要求。在Unicode裏,全部的字符被一視同仁,漢字再也不使用「兩個擴展ASCII」,而是使用「1個Unicode」來表示,也就是說,全部的文字都按一個字符來處理,它們都有一個惟一的Unicode碼。Unicode用數字0-0x10FFFF來映射這些字符,最多能夠容納1114112個字符,或者說有1114112個碼位(碼位就是能夠分配給字符的數字)。
提到Unicode不能不提UCS(通用字符集Universal Character Set)。UCS是由ISO制定的ISO 10646(或稱ISO/IEC 10646)標準所定義的標準字符集。UCS-2用兩個字節編碼,UCS-4用4個字節編碼。Unicode是由unicode.org制定的編碼機制,ISO與unicode.org是兩個不一樣的組織, 雖然最初制定了不一樣的標準; 但目標是一致的。因此自從unicode2.0開始, unicode採用了與ISO 10646-1相同的字庫和字碼, ISO也承諾ISO10646將不會給超出0x10FFFF的UCS-4編碼賦值, 使得二者保持一致。你們簡單認爲UCS等同於Unicode就能夠了。
在Unicode中:漢字「字」對應的數字是23383。在Unicode中,咱們有不少方式將數字23383表示成程序中的數據,包括:UTF-八、UTF-1六、UTF-32。UTF是「UCS Transformation Format」的縮寫,能夠翻譯成Unicode字符集轉換格式,即怎樣將Unicode定義的數字轉換成程序數據。例如,「漢字」對應的數字是0x6c49和0x5b57,而編碼的程序數據是:
1
2
3
BYTE data_utf8[] = {0xE6, 0xB1, 0x89, 0xE5, 0xAD, 0x97}; // UTF-8編碼
WORD data_utf16[] = {0x6c49, 0x5b57}; // UTF-16編碼
DWORD data_utf32[] = {0x6c49, 0x5b57}; // UTF-32編碼
這裏用BYTE、WORD、DWORD分別表示無符號8位整數,無符號16位整數和無符號32位整數。UTF-八、UTF-1六、UTF-32分別以BYTE、WORD、DWORD做爲編碼單位。「漢字」的UTF-8編碼須要6個字節。「漢字」的UTF-16編碼須要兩個WORD,大小是4個字節。「漢字」的UTF-32編碼須要兩個DWORD,大小是8個字節。根據字節序的不一樣,UTF-16能夠被實現爲UTF-16LE或UTF-16BE,UTF-32能夠被實現爲UTF-32LE或UTF-32BE。
下面介紹UTF-八、UTF-1六、UTF-3二、BOM。
UTF-8
UTF-8以字節爲單位對Unicode進行編碼。從Unicode到UTF-8的編碼方式以下:
Unicode編碼(16進制) UTF-8 字節流(二進制)
000000 – 00007F 0xxxxxxx
000080 – 0007FF 110xxxxx 10xxxxxx
000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 – 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-8的特色是對不一樣範圍的字符使用不一樣長度的編碼。對於0×00-0x7F之間的字符,UTF-8編碼與ASCII編碼徹底相同。UTF-8編碼的最大長度是4個字節。從上表能夠看出,4字節模板有21個x,便可以容納21位二進制數字。Unicode的最大碼位0x10FFFF也只有21位。總結了一下規律:UTF-8的第一個字節開始的1的個數表明了總的編碼字節數,後續字節都是以10開始。由上面的規則能夠清晰的看出UTF-8編碼克服了中文編碼的兩個問題。
例1:「漢」字的Unicode編碼是0x6C49。0x6C49在0×0800-0xFFFF之間,使用3字節模板了:1110xxxx 10xxxxxx 10xxxxxx。將0x6C49寫成二進制是:0110 1100 0100 1001, 用這個比特流依次代替模板中的x,獲得:11100110 10110001 10001001,即E6 B1 89。
例2:Unicode編碼0x20C30在0×010000-0x10FFFF之間,使用用4字節模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。將0x20C30寫成21位二進制數字(不足21位就在前面補0):0 0010 0000 1100 0011 0000,用這個比特流依次代替模板中的x,獲得:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。
UTF-16
UTF-16編碼以16位無符號整數爲單位。咱們把Unicode編碼記做U。編碼規則以下: 若是U<0×10000,U的UTF-16編碼就是U對應的16位無符號整數(爲書寫簡便,下文將16位無符號整數記做WORD)。中文範圍 4E00-9FBF,因此在UTF-16編碼裏中文2個字節編碼。若是U≥0×10000,咱們先計算U’=U-0×10000,而後將U’寫成二進制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16編碼(二進制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。
UTF-32
UTF-32編碼以32位無符號整數爲單位。Unicode的UTF-32編碼就是其對應的32位無符號整數。
字節序
根據字節序(對字節序不太瞭解的同窗請參考http://en.wikipedia.org/wiki/Endianness)的不一樣,UTF-16能夠被實現爲UTF-16LE(Little Endian)或UTF-16BE(Big Endian),UTF-32能夠被實現爲UTF-32LE或UTF-32BE。例如:
Unicode編碼 UTF-16LE UTF-16BE UTF-32LE UTF-32BE
0x006C49 49 6C 6C 49 49 6C 00 00 00 00 6C 49
0x020C30 43 D8 30 DC D8 43 DC 30 30 0C 02 00 00 02 0C 30
那麼,怎麼判斷字節流的字節序呢?Unicode標準建議用BOM(Byte Order Mark)來區分字節序,即在傳輸字節流前,先傳輸被做爲BOM的字符」零寬無中斷空格」。這個字符的編碼是FEFF,而反過來的FFFE(UTF-16)和FFFE0000(UTF-32)在Unicode中都是未定義的碼位,不該該出如今實際傳輸中。下表是各類UTF編碼的BOM:
UTF編碼 Byte Order Mark
UTF-8 EF BB BF
UTF-16LE FF FE
UTF-16BE FE FF
UTF-32LE FF FE 00 00
UTF-32BE 00 00 FE FF
總結一下,ISO與unicode.org都敏銳的意識到只有爲世界上每種語言中的每一個字符設定統一而且惟一的二進制編碼才能完全解決計算機世界信息交流中編碼衝突的問題。由此誕生了UCS和unicode,而這兩個規範是一致的。在Unicode裏,全部的字符被一視同仁,也就是說,全部的文字都按一個字符來處理,它們都有一個惟一的Unicode碼。UTF-八、UTF-1六、UTF-32分別定義了怎樣將Unicode定義的數字轉換成程序數據。UTF-8以字節爲單位對Unicode進行編碼,一個英文字符佔1個字節,漢字佔3個字節;UTF-16以16位無符號整數爲單位對Unicode進行編碼,中文英文都佔2個字節;UTF-32以32位無符號整數爲單位對Unicode進行編碼,中文英文都佔4個字節。能夠在http://www.unicode.org/charts/unihan.html 查看漢字的unicode碼以及UTF-八、UTF-1六、UTF-32編碼。
中文二進制存儲
介紹了這麼多的編碼知識,真正的文件內容是什麼樣子的呢?下面咱們就經過實驗看看在筆者Linux機器上 「中文」這兩個字在不一樣的編碼下保存的文件內容。下面是個人實驗過程,有興趣的同窗能夠在本身的機器上重作一下。window平臺上的狀況相似這裏就不贅述了。
實驗須要須要使用2個工具:
od 查看文件內容:http://www.gnu.org/software/coreutils/manual/html_node/od-invocation.html
iconv 編碼轉換工具:http://www.gnu.org/software/libiconv/
漢字 Unicode(ucs-2)10進製表示 Utf-8 Utf-16 Utf32 區位碼 GB2312/GBK/GB18030
中 20013 E4 B8 AD 4E2D 00004E2D 5448 D6D0
文 25991 E6 96 87 6587 00006587 4636 CEC4
機器環境:
os: Red Hat Enterprise Linux AS release 4
Cpu: Intel(R) Xeon(R) CPU
locale:LC_ALL=zh_CN.utf-8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//生成utf8編碼下的文件
echo –n "中文" > foo.utf8
//檢查foo的內容:
od -t x1 foo.utf8
0000000 e4 b8 ad e6 96 87
//轉換爲utf16編碼
iconv -f utf-8 -t utf-16 foo.utf8 > foo.utf16
//查看foo.utf16內容
od -t x1 foo.utf16
0000000 ff fe 2d 4e 87 65
Ff fe是BOM(還記得嗎?經過BOM來字節流的字節序),其他部分的確是UTF-16LE編碼的內容
//轉換爲utf32編碼
iconv -f utf-16 -t utf-32 foo.utf16 > foo.utf32
//查看foo.utf32內容
od -t x1 foo.utf32
0000000 ff fe 00 00 2d 4e 00 00 87 65 00 00
Ff fe是BOM,的確是UTF-32LE編碼的內容
//轉換爲gb2312編碼
iconv -f utf-8 -t gb2312 foo.txt > foo.gb2312
od -t x1 foo.gb2312
0000000 d6 d0 ce c4
//轉換爲GBK編碼
iconv -f utf-8 -t gbk foo.txt > foo.gbk
od -t x1 foo.gbk
0000000 d6 d0 ce c4
//轉換爲GB18030編碼
iconv -f utf-8 -t gb18030 foo.txt > foo.gb18030
od -t x1 foo.gb18030
0000000 d6 d0 ce c4
C語言中文處理
先明確一個概念:程序內部編碼和程序外部編碼。程序內部編碼指的是中文字符在程序運行時在內存中的編碼形式。程序外部編碼則是中文字符在存儲或者傳輸時的編碼形式。程序外部編碼的最直觀的例子就是當把中文存儲到硬盤文件中時選擇的編碼。
根據程序內部編碼和程序外部編碼是否一致,C/C++的中文處理有兩種常見的方式:
內外編碼相同。輸入輸出時不須要考慮編碼轉換,程序內部處理時把中文字符當作普通的2進制數據流進行處理。
內外編碼不一樣。輸入輸出的時候根據應用須要選擇合適的編碼格式進行編碼轉換;程序內部統一編碼處理。
方法1的優勢不言而喻,因爲內外統一,不須要進行轉換。不足是若是不是C標準庫支持的編碼方式,那麼字符串處理函數須要本身實現。好比說標準strlen函數不能計算中文編碼&UTF-8等的字符串長度,而須要根據編碼標準自行實現。GBK等中文編碼除了計算字符串長度的函數外,字符串匹配函數也要本身實現(緣由看上文中文編碼總結)。當須要支持的編碼格式不斷增多時,處理函數的開發和維護就須要付出更大的代價。
方法2針對方法1的不足加以改進。在程序內部能夠優先選擇C標準庫支持的編碼方式,或者根據須要本身實現對某一特定編碼格式的完整支持,這樣任何編碼均可以先轉換爲支持的編碼,代碼通用性比較好。
那麼C標準庫對中文編碼的支持如何呢?目前Linux平臺通常使用GNU C library,內建了對單字節的char和寬字符wchar_t的支持。Char你們都很熟悉了,處理中文須要的wchar_t要重點介紹一下。從實現上來講在linux平臺上能夠認爲wchar_t是4byte的int,內部存儲字符的UTF32編碼。因爲標準庫已經內建了對wchar_t比較完備的支持,如使用wcslen 計算字符串長度,使用wcscmp進行字符串比較等等。因此比較簡單的方式是使用上面的方法2,同時選擇wchar_t做爲內部字符的表示。作到這一點仍是比較容易的,在輸入輸出的時候經過mbrtowc/wcrtomb 進行單個字符的內外編碼轉換,以及經過mbsrtowcs/wcsrtombs 進行字符串的內外編碼轉換便可。這裏須要注意兩點:
代碼中字符串常量的表示不一樣。舉例說明:Char c=’a’; Wchar_t wc=L’中’;
上面兩組函數的轉換是依賴locale設置的,即locale決定了外部編碼的類型。確切的說是LC_CTYPE決定了外部編碼的類型。默認狀況下程序啓動時使用標準「C」locale,而不是LC系列的環境變量指定的。因此須要首先調用下面的函數:setlocale (LC_ALL, 「」);這樣程序就使用了用戶經過設置LC系列環境變量選擇的Locale。
關於locale的話題比較大,這裏就不深刻了,留待下一篇文章吧.
上面的方法很完美,是嗎?不是嗎?獲得這麼多的好處不是無代價的,最明顯的代價就是內存,任何一個字符,無論中文仍是英文若是保持在wchar_t裏就須要4個byte,就這一個理由就足以限制了這個方案在關注內存使用的應用場景下的使用。
Python的中文處理
對Python來講因爲內建unicde的支持,因此採用輸入輸出的時候進行轉換,內部保持unicode的方式使用是個不錯的方案。http://docs.python.org/tutorial/introduction.html#unicode-strings這裏做爲起點,有興趣的同窗自學吧。
編碼選擇建議:
只有英文:絕不猶豫選擇內外編碼都選擇ASCII,通用且存儲代價小。
主要存中文,對存儲大小比較敏感:內外部編碼根據文字使用範圍選擇GB2312或者GBK,自行實現使用到的字符串處理函數。
通用性第一,處理簡單:外部選擇UTF-8,內部可使用UTF-8或者UTF-32(即wchar_t)
參考資料:
http://baike.baidu.com/view/25492.htm
http://baike.baidu.com/view/25421.htm
http://baike.baidu.com/view/40801.htm
http://www.sac.gov.cn/SACSearch/search?channelid=160591&templet=gjcxjg_detail.jsp&searchword=STANDARD_CODE=’GB%202312-1980′&XZ=Q
http://www.sac.gov.cn/SACSearch/search?channelid=160591&templet=gjcxjg_detail.jsp&searchword=STANDARD_CODE=’GB%2018030-2005′&XZ=Q
http://www.sac.gov.cn/SACSearch/search?channelid=160591&templet=gjcxjg_detail.jsp&searchword=STANDARD_CODE=’GB%2013000-2010′&XZ=Q
http://www.unicode.org/
http://www.ibm.com/developerworks/cn/linux/i18n/unicode/linuni/
http://www.gnu.org/software/libc/manual/html_node/index.html
http://www.gnu.org/software/libiconv/
Filed under - 搜索引擎 19 Comments so far.
標籤:GB2312,GBK,Unicode, 中文處理, 中文編碼
相關文章
neo4j 底層存儲結構分析(2)
解讀Google分佈式鎖服務
Doclist壓縮方法簡介
Protocol Buffers的應用與分析
19 RESPONSES