Oracle字符集總結歸納

使用常用的gbk,utf8以及ascii說明(oracle中對應hs16gbk,al32utf8,us7ascii)。
其中,ascii爲經典的單字節編碼,採用7位,只能表示128個常用字符以及計算機控制字符,不能顯示中文等字符;gbk爲中國大陸標準,16位,簡體中文字符集標準;utf8爲unicode的一種最常用實現,兼容ascii的字符使用1位,其餘字符都採用3位,可以表示任何字符。Utf8其實已經可以稱爲國際標準字符集編碼,應首先考慮使用utf8。需要說明的是,雖然gbk跟utf8都能表示中文字符,但每個字符的編碼是不一樣的,所以不能認爲是兼容的。

其實,oracle的字符系統很簡單,主要由3部分組成:
[b]客戶終端字符集[/b],[b]nls_lang環境變量字符集[/b],[b]數據庫使用的字符集[/b]。其中,客戶終端各種各樣,比如windows下cmd,unix/linux下terminal/console,甚至如toad等工具也可以視爲一個終端。我們使用windows cmd舉例說明,cmd使用了gbk編碼(好像不能修改)

nls_lang環境變量決定了終端與服務器連接時,要不要轉換字符集,如果nls_lang和服務器端數據庫使用的編碼不一樣,那就要進行轉碼;如果設爲一樣,就不會發生轉碼。
比如一個終端cmd查詢一張數據庫表,而其nls_lang設爲american_america.zhs16gbk,而數據庫爲american_america.al32utf8,則數據庫中的數據傳到終端時,首先要進行utf8到gbk的轉碼。

而轉碼又有兩種情況,a.就是一個字符在兩中編碼裏都有,就是具體的碼文不一樣;b.轉碼時發現一個字符在另一種編碼裏沒有,則直接用替代字符代替,而這一般就是‘?’字符了。這裏要注意的是,我們一般所說的亂碼有2種,一種就是b所說的‘?’,而另一種出現的奇奇怪怪的字符就是由於nls_lang設置等轉碼原因造成的。
而數據庫使用的字符集就是數據庫實際使用的字符集。

現舉例說明:

先創建Oracle數據庫utf8使用al32utf8編碼,使用cmd(gbk),分以下幾種情況:
a. 設nls_lang也爲zhs16gbk,則insert中文時,服務器發現客戶端nls_lang爲gbk,則要轉換映射。輸入“我靠”;因爲終端爲cmd,所以輸入的字符集都是gbk編碼的,假設“我靠”的gbk編碼爲0xaabb,則0xaabb要映射爲utf8(假設“我靠”的utf8編碼爲0xccddee)編碼,存入數據庫,則數據中保存了utf8編碼的“我靠“。然後,再使用cmd進行select查詢,數據庫發現nls_lang爲zhs16gbk,則把”我靠“從utf8碼0xccddee轉爲gbk的0xaabb,然後正常在cmd中顯示”我靠“兩字。

b. 設nls_lang也爲al32utf8,則從cmd輸入“我靠“時,服務器發現客服端nls_lang爲utf8,則它不進行轉換,直接存0xaabb進數據庫;當select時,因爲也不要轉換,所以直接發0xaabb給終端,而0xaabb在終端cmd(gbk)裏顯示爲“我靠”,也能正常顯示。但要注意的是,這時對服務器來說,存在數據庫裏的內容其實是不正確的,不是正確的“我靠”的utf8編碼;而只是存了個“我靠“的gbk編碼。
c. 先設nls_lang爲utf8,輸入完“我靠“後,改nls_lang爲gbk,再做select,發現顯示爲亂碼了。這種就是前面說的因爲轉碼問題造成的亂碼。因爲數據庫一開始存的是”我靠“的gbk碼0xaabb;然後做select時,因爲使用了改爲gbk的nls_lang,則數據庫要進行轉碼了,就是要把utf8中的0xaabb轉爲gbk碼,假設utf8中0xaabb表示的是”B??“,那就是要把”B??“轉爲gbk碼,然後在cmd中用gbk顯示了”B??“亂碼。



然後,我們再次創建一個us7ascii碼的數據庫chr。
d. 設nls_lang爲american_america.us7ascii,輸入“我靠“,然後再select,發現竟然能正常顯示中文。這是因爲由於數據庫編碼和nls_lang編碼一樣,所以不進行轉碼,直接把”我靠“的gbk碼0xaabb輸入了數據庫。而select時,由於也不要轉碼,所以又直接把0xaabb傳了回來,在cmd(gbk)中就顯示了”我靠“。

e. 延續d的情況,再設nls_lang爲american_america.zhs16gbk,再做select,發現返回NR??,這種情況還是前面所說的轉碼造成的亂碼,因爲gbk是兼容ascii的,只是這裏還是進行了錯誤的轉碼,因爲ascii中的0xaabb轉碼成gbk顯示,就是NR??。
f. 設nls_lang爲american_america.zhs16gbk,輸入“我靠“,然後再select,發現返回兩個??;這是因爲”我靠“的gbk編碼要轉碼到uscii時,因爲uscii裏沒有”我靠“兩個字符的編碼,所以不兼容,直接用兩個?代替;而select時,因爲ascii裏的?和gbk裏的?是兼容的,所以轉碼後,cmd裏也顯示兩個?。


綜上所述,可以得出如下規範:
1. 數據庫字符應該至少使用gbk,最好是utf8字符集,這樣至少基本上不會出現全問號亂碼的情況,因爲不會出現找不到對應字符,而採用?等字符代替的情況。情況f其實就是這樣。
2. 應該設置nls_lang和終端使用一樣的編碼,這樣數據庫也能正確知道終端使用的編碼,就能正確得轉碼,就不大會把錯誤的編碼存進數據庫,情況b,c,d,e就是設錯了nls_lang才造成了問題。這裏面,還有種情況,就是不設nls_lang時,好像oracle默認會把它當作和終端編碼一樣(需要確認)。
3. 儘量使得3個編碼都一樣,這樣就能避免編碼轉換造成的性能損失。


其實,不僅oracle,別的一些數據庫,甚至是一些編程工具,如jsp/servlet等都有類似的編碼體制,基本上都是終端(如jsp裏爲編輯器),環境變量(jsp裏爲pageencoding指令),服務器端編碼(jsp爲瀏覽器等);再比如oracle做imp時,終端編碼就爲dmp包中的編碼(不一定爲原數據庫編碼,因爲有可能它在exp時,已經轉過碼了),環境變量編碼還是nls_lang,而服務端就是要實行imp的數據庫的編碼了。
有興趣的話,大家可以研究下mysql的字符集機制,它本質上還是使用三位一體的編碼機制,但劃分的更細。

From:[url]http://tanrenjie.spaces.live.com/blog/cns!599006622366184E!343.entry[/url]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章