深入解析字符集和字元編碼

husthxd發表於2017-10-13

字元(Character)是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。計算機要準確的處理各種字符集文字,需要進行編碼,以便計算機能夠識別和儲存各種字元。

0x01 字符集

字符集(Character set)是多個字元的集合,字符集種類較多,每個字符集包含的字元個數不同,常見字符集名稱:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。

字符集從表示字元使用位元組的數目來區分,可以分為單位元組字符集和多位元組字符集。

單位元組字符集

顧名思義,單位元組字符集是使用一個位元組表示字元的字符集。常見的包括Latin(又稱ISO8859)系列字符集(如Latin1、ISO8859P1等)、ASCII等。

由於只有一個位元組,單位元組字符集最多隻能表示256個字元。

多位元組字符集

多位元組字符集使用一個或多個位元組表示一個字元。

支援中文的字符集

支援中文的字符集包括國標系列字符集(GB2312、GBK、GB18030)和Unicode。

Latin系列字符集的編碼範圍是00-FF,因此無論是單位元組表示的字元還是多位元組表示的字元,均可以儲存任意字元包括中文。

0x02 字元編碼

相同的字元,在不同的字符集下可能有不同的編碼。比如“中文”這兩個字元,在GBK字符集的編碼為d6,d0,ce,c4,在Unicode字符集中使用UTF8編碼為e4,b8,ad,e6,96,87。相同的字元可能有不同的編碼,在字符集不同的操作環境中交換資料可能會發生編碼轉換,如果轉換失敗就會出現亂碼。

編碼轉換

編碼轉換在操作環境字符集不同的情況下發生。操作環境為作業系統為中文版Windows,預設的字符集為GBK,而在作業系統為Linux,則使用的字符集為UTF8,在這兩者之間進行資料交換時,就可能發生字元編碼轉換。

比如我們在Windows平臺(使用預設的環境變數)下啟動SQL*PLUS從遠端字符集為UTF8的Oracle資料庫中查詢含有中文的資料時,會發生UTF8字元編碼向GBK字符集的編碼轉換。這個轉換過程在下一章節會詳細討論。

亂碼

字元編碼如果轉換不成功,就會產生亂碼。在編碼轉換中我們提到,字元從UTF8轉換為GBK,由於UTF8和GBK均存在相應的中文字元編碼,這時候轉換是成功的,不會產生亂碼,如“中文”兩個字元,從“e4,b8,ad,e6,96,87”轉換為“d6,d0,ce,c4”編碼。但如果從GBK往Latin1轉換或者Latin1往GBK進行轉換時,由於兩種字符集不相容,轉換就會出現亂碼。

通常來說,無法轉換的字元會變為目標字符集中的疑問字元,在ASCII下是3f,GBK下是a3,bf,在ISO8859P1下是bf。

另外,值得一提的是,如果是透過JDBC驅動獲取資料,如果字元無法轉換為UTF16(Java內部使用的字元編碼),字元可能會變成null值。

0x03 Oracle中的字元編碼轉換

本章節以Oracle資料庫為例詳細解釋在使用SQL*PLUS時,Oracle如何處理字元編碼的轉換。

操作模型

深入解析字符集和字元編碼

操作模型如上圖所示,客戶端環境包括作業系統OS、應用環境AppEnv、客戶端工具SQL*PLUS和資料庫驅動(OCI Driver)以及環境變數NLS_LANG。服務端環境包括作業系統OS,Oracle資料庫。

舉個例子,我們在中文版Windows中,透過CMD開啟SQL*PLUS,連線到作業系統為CentOS 6.5,字符集為GBK的資料庫。這時候操作模型的各種要素,客戶端為:OS=Windows(字符集為GBK),AppEnv=CMD(預設CodePage=936,可透過chcp修改),NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK(預設值);服務端為:OS=Linux(字符集為Unicode,字元編碼為UTF8),DB Character Set=GBK。

在客戶端和服務端之間,是否需要字元轉換,由客戶端環境變數NLS_LANG進行控制,如NLS_LANG中定義的字符集與資料庫字符集一致,則不發生轉換,否則發生轉換。設定該環境變數的最佳實踐為:資料庫字符集為單位元組字符集,環境變數字符集與資料庫字符集一致;資料庫字符集為多位元組字符集,環境變數字符集與客戶端操作環境字符集一致

下面我們按照各種不同的設定看看在寫入資料時發生的字元編碼轉換。

#1:客戶端為Win7/CodePage=936;服務端為UTF8資料庫;NLS_LANG=.ZHS16GBK

如下圖所示:

深入解析字符集和字元編碼

由於環境變數字符集與資料庫伺服器字符集不一致,這時候會發生編碼轉換,從GBK轉換為UTF8,由於字元的實際編碼為GBK而且GBK和UTF8兩者相容,因此轉換成功,儲存在資料庫中的字元編碼為正確的UTF8編碼。

深入解析字符集和字元編碼

#2:客戶端為Win7/CP=936;服務端為GBK資料庫;NLS_LANG=.AL32UTF8

如下圖所示:

深入解析字符集和字元編碼

由於環境變數字符集與資料庫伺服器字符集不一致,這時候會發生編碼轉換,從UTF8轉換為GBK,由於字元的實際編碼為GBK,將會把這些字元編碼視為UTF8編碼往GBK轉換,這時候會出現轉換不成功的情況。

深入解析字符集和字元編碼

如上圖所示,字元“中文”的GBK編碼為d6,d0,ce,c4,均為不合法的UTF8編碼,從UTF8轉換為GBK時會轉換為a3,bf;字元“涓璍”的GBK為“e4,b8,ad,4c”,前面3個位元組為合法的UTF8編碼(字元“中”的UTF8編碼),因此從UTF8轉換為GBK時,轉換為字元“中L”(ASCII 4c為字元L)。

#3:客戶端為Win7/CP=936;服務端為WE8ISO885P1資料庫;NLS_LANG=.ZHS16GBK

如下圖所示:

深入解析字符集和字元編碼

由於環境變數字符集與資料庫伺服器字符集不一致,這時候會發生編碼轉換,從GBK轉換為ISO8859P1,由於字符集不相容,字元轉換失敗,中文字元會變為bf。

深入解析字符集和字元編碼

在GBK字符集中,"bf,bf"為合法的字元編碼,也就是中文字元“靠”的編碼,這就是有時候我們看到一堆的“靠”字的原因所在。

0x04 結語

字元或其他資訊的編碼是計算機最基本的知識,因為只有經過編碼之後的資訊才能儲存在計算機中,掌握好這些基礎知識才能更好的理解其他更高階的內容。





來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6906/viewspace-2145974/,如需轉載,請註明出處,否則將追究法律責任。

相關文章