編者按: 中文亂碼是一個(gè)一直困擾我國很多程序員的問(wèn)題。由于各種開(kāi)發(fā)平臺(語(yǔ)言)、數據庫在處理中文字符時(shí)機制不盡相同,導致這一問(wèn)題解決起來(lái)十分復雜。本期我們從眾多來(lái)稿中挑選了一組有關(guān)中文字符處理的文章,讀者也許可以從這些解決方法中得到一些啟發(fā)。
Oracle在數據轉儲時(shí)的字符集問(wèn)題
東北大學(xué)秦皇島分校 王全海
作為一個(gè)Oracle數據庫的用戶(hù),對于Export和Import兩個(gè)命令絕對不會(huì )感到陌生,因為這二者正是我們經(jīng)常用 于數據備份和恢復的工具。但在使用這兩個(gè)命令過(guò)程中所發(fā)生的Oracle字符集問(wèn)題,常給一些Oracle使用者帶來(lái)不必要的麻煩和不必要的數據損失。本 文將就Export和Import過(guò)程中Oracle字符集的轉換規律及使用這兩個(gè)命令的注意事項做一總結。
字符集轉換的原因 Export、Import過(guò)程如下圖所示,從這個(gè)示意圖中可以看到有四處關(guān)系到字符集,而這四處字符集的不一致恰恰是導致Oracle進(jìn)行字符集轉換的原因。
源數據庫字符集;
Export過(guò)程中用戶(hù)會(huì )話(huà)字符集;
Import過(guò)程中用戶(hù)會(huì )話(huà)字符集;
目標數據庫字符集。
在Export和Import過(guò)程中,如果存在影響字符集轉換的四因素不一致,則可能發(fā)生Oracle字符集轉換,即:
在Export過(guò)程中,如果源數據庫字符集與Export用戶(hù)會(huì )話(huà)字符集不一致,會(huì )發(fā)生字符集轉換,并在導出的二進(jìn)制格式Dmp文件的頭部幾個(gè)字節中存儲Export用戶(hù)會(huì )話(huà)字符集的ID號。在這個(gè)轉換過(guò)程中可能發(fā)生數據的丟失。
例1: 如果源數據庫使用ZHS16GBK,而Export用戶(hù)會(huì )話(huà)字符集使用US7ASCII,由于ZHS16GBK是8位字符集,而US7ASCII是7位字 符集,這個(gè)轉換過(guò)程中,中文字符在US7ASCII中不能夠找到對等的字符,所以所有中文字符都會(huì )丟失而變成“?? ”形式,即這種轉換后生成的Dmp文件已經(jīng)發(fā)生了數據丟失。
例2: 如果源數據庫使用ZHS16GBK,而Export用戶(hù)會(huì )話(huà)字符集使用ZHS16CGB231280,但由于ZHS16GBK字符集是 ZHS16CGB231280字符集的超集,這個(gè)過(guò)程中絕大部分字符都能夠正確轉換,只有一些超出ZHS16CGB231280字符集的字符變?yōu)?#8220;?? ”形式。如果源數據庫使用ZHS16CGB231280字符集,而Export用戶(hù)會(huì )話(huà)使用ZHS16GBK字符集,則轉換過(guò)程能夠完全轉換成功。
在Import向目標數據庫轉換過(guò)程中,其字符集發(fā)生轉換的情況正好與Export過(guò)程相反,這里不再詳述。
在Export導出的Dmp文件中,含有Export用戶(hù)會(huì )話(huà)字符集。在Import過(guò)程中,首先發(fā)生的是Dmp文件字符集(即 Export用戶(hù)會(huì )話(huà)字符集)向Import用戶(hù)會(huì )話(huà)字符集的轉換。如果這個(gè)轉換過(guò)程不能正確完成,Import向目標數據庫的導入過(guò)程也就不能完成。
進(jìn)行字符集的正確轉換 通常情況下,我們在使用Oracle的Export和Import過(guò)程中,并不希望發(fā)生字符的轉換,但有時(shí)這種轉換卻是必 要的。如我們在安裝Oracle數據庫時(shí),選擇ZHS16CGB231280字符集,由于這種字符集是一種中文小字符集,對于一些漢字不能夠正確表示,這 需要通過(guò)使用ZHS16GBK字符集得到解決,此時(shí)就要進(jìn)行字符集的轉換。
為了確保Export、Import過(guò)程中,Oracle字符集不發(fā)生轉換或正確轉換,建議最好在進(jìn)行這個(gè)過(guò)程 前,檢查一下源數據庫字符集與Export用戶(hù)會(huì )話(huà)字符集是否一致,源數據庫字符集與目標數據庫字符集是否一致,目標數據庫字符與Import用戶(hù)會(huì )話(huà)字 符集是否一致。如果能夠保證這四個(gè)字符集是一致的,則在Export、Import過(guò)程中,Oracle字符集就不用發(fā)生轉換。
可用以下辦法檢查數據庫字符集:
通過(guò)InitXXXX.ora文件進(jìn)行查看;
借助SQL語(yǔ)句查看: SELECT NAME,VALUE$ FROM SYS.PROPS$ WHERE NAME=‘NLS_CHARACTERSET’。
對于Export、Import用戶(hù)會(huì )話(huà)字符集,在Windows系統中也可以通過(guò)注冊表中的NLS_LANG進(jìn)行查看或修改,對于Unix系統則可通過(guò)設置用戶(hù)的環(huán)境變量NLS_LANG來(lái)查看或修改。
特別要注意的是,Oracle數據庫字符集通常是在創(chuàng )建時(shí)確定,一旦存儲用戶(hù)數據后就不要再修改了,因為其數據都是使用該字符集進(jìn)行存儲的,改換其他字符集之后,原有數據就不能夠正確表示了。但如果確實(shí)想進(jìn)行字符集改變,則可通過(guò)以下幾步來(lái)實(shí)現:
備份數據庫后刪除原數據(可物理備份,如使用Export,請注意確保字符集不發(fā)生轉換或數據無(wú)損失);
使用Internal用戶(hù)更新sys.props$表中的字符集:
Update sys.props$ set name=‘Dest.CharSet’ Where name=‘NLS_CHARACTERSET’; COMMIT;
重啟數據庫;
恢復數據。
下面字符集之間的轉換是可行的:
字符集子集向字符集父集轉換是可行的,如ZHS16CGB231280向ZHS16GBK轉換;而字符集父類(lèi)向字符集子集進(jìn)行轉換時(shí),會(huì )損失部分數據。
只包含英文字符數據的雙字節字符集也可向單字節字符集轉換,如ZHS16GBK(English Only)可以向US7ASCII正確轉換。
編碼范圍相同的單字節字符集之間通??梢赃M(jìn)行相互轉換。
請注意,這里所說(shuō)的沒(méi)有數據損失,是指一種字符集A轉換成另一種字符集B之后,可以再從字符集B正確轉換成字符集A或字符集B能夠正確表示字符集A中轉換過(guò)來(lái)的數據。
字符集對程序的影響 根據一個(gè)字符需要多少位字節來(lái)表示,可以把字符集分為單字節字符集和多字節字符集。其中,單字節字符集又分為7位字符集和 8位字符集。單字節7位編碼字符集有US7ASCⅡ,單字節8位編碼字符集有符合ISO 8859-1標準規定的WE8ISO8859P1等。多字節編碼又分為固定長(cháng)度(長(cháng)度大于或等于2)編碼模式和不固定長(cháng)度編碼模式。多字節編碼字符集中的 ZHS16GBK、ZHS16CGB231280、JA16SJIS等是采用兩個(gè)字節表示一個(gè)字符的字符集,又叫雙字節字符集。
一個(gè)英文字母是一個(gè)字符,一個(gè)中文漢字是幾個(gè)字符呢?我們知道,一個(gè)中文漢字是雙字節字符,但它有幾個(gè)字符與其 數據庫字符集有關(guān)。如果數據庫字符集使用單字節US7ASCII,則一個(gè)中文漢字是二個(gè)字符;如果數據庫字符集使用雙字節字符集ZHS16GBK,則一個(gè) 中文漢字是一個(gè)字符。有關(guān)這一點(diǎn)可以使用Oracle的函數Substr得到證明。
使用US7ASCⅡ字符集時(shí):
Select substr(‘東北大學(xué)’,1,2) from dual;
語(yǔ)句執行結果返回‘東’。
使用ZHS16GBK字符集時(shí):
Select substr(‘東北大學(xué)’,1,2) from dual;
語(yǔ)句執行結果返回‘東北’。 選擇合適的數據庫字符集 選擇數據庫字符集時(shí)應考慮以下事項:
1.數據庫需要支持什么語(yǔ)言 在為數據庫選擇字符集時(shí),常會(huì )發(fā)現幾種字符集都適合你當前語(yǔ)言需求,如簡(jiǎn)體中文就有ZHS16GBK和 ZHSCGB231280等字符集可供選擇,應選擇哪種?在選擇字符集時(shí),應考慮到數據庫將來(lái)的系統需求。如果知道將來(lái)數據庫要擴展支持不同的語(yǔ)言,選擇 一個(gè)范圍較廣的字符集會(huì )是一個(gè)更好的主意。
2.系統資源與應用之間的互作用性 選擇的數據庫字符集應保證操作系統與應用之間的無(wú)縫連接。如果選擇的字符集不是操作系統有效的字符集,則系統就需要在這兩 者之間做字符轉換。在這種字符轉換過(guò)程中,就有可能發(fā)生一些字符丟失現象。從一種字符集A向另一種字符集B轉換過(guò)程中,A中的字符必須在B中可以找到等價(jià) 的字符,否則就會(huì )以“?”來(lái)代替。從這個(gè)意義上說(shuō),如果兩種字符集編碼范圍是相同的,則可以相互轉換。
字符集轉換過(guò)程中會(huì )影響系統性能,因此,應保證客戶(hù)端和服務(wù)器端有相同的字符集以避免字符集轉換,也可以提高一定的系統性能。
3.系統的性能要求 不同的數據庫字符集對于數據庫的性能是有一定影響的。為了得到最好的數據庫性能,選擇的數據庫字符集應避免字符轉換,并且要選擇對于期望的語(yǔ)言有最高效的編碼效率。通常,單字節字符集比多字節字符集有更優(yōu)的性能表現,在空間需求方面也更小些。
4.其他一些限制 在為數據庫選擇一個(gè)合適的字符集時(shí),應參考Oracle對應版本的相關(guān)文檔,檢查Oracle對于一些字符集的限制。如 Oracle 8.1.5版本中,以下字符集是不能使用的: JA16EUCFIXED、ZHS16GBKFIXED、JA16DBCSFIXED、KO16DBCSFIXED、ZHS16DBCSFIXED、 JA16SJISFIXED、ZHT32TRISFIXED。
綜上所述,正確理解Oracle字符集的轉換過(guò)程,可以使我們避免不必要的麻煩和數據損失。合理利用Oracle字符集的轉換過(guò)程,也可以幫助我們正確地從一種字符集轉換到另一種字符集,以滿(mǎn)足我們各種不同的應用需求。