1,、字符集的一些基本知識
字符集有很多種,,最初的字符集是ASCII,由于ASCII支持的字符很有限,,因此隨后又出現(xiàn)了很多的編碼方案,,這些編碼方案大部分都是包括了ASCII的,。EBCDIC編碼是另一個比較基本的編碼,,它的部分字符采用了和ASCII不同的編碼值,因此兩者是不兼容的基本編碼方案,。采用EBCDIC編碼的比較少,,目前主要是IBM 的系統(tǒng)采用,如AS400及S390系統(tǒng),,大部分的系統(tǒng)都是基于ASCII編碼的,。
由于亞洲國家的字符集相對復(fù)雜一些,因此一般都使用了兩個及以上的字節(jié)進(jìn)行編碼的方案,。對于簡體中文,,GB2312碼是國家1981年實(shí)施的編碼標(biāo)準(zhǔn),通行于大陸,。新加坡等地也使用此編碼,。GBK編碼是GB2312碼的擴(kuò)展,是1995年發(fā)布的指導(dǎo)性規(guī)范,,它在字匯一級支持 ISO/IEC 10646-1 和GB 13000-1 的全部中日韓 (CJK) 漢字(20902字),。目前最新的漢字字符集是2000年的GB18030,它是取代GBK1.0的正式國家標(biāo)準(zhǔn),。該標(biāo)準(zhǔn)收錄了27484個漢字,,同時還收錄了藏文、蒙文,、維吾爾文等主要的少數(shù)民族文字,。目前簡體WINDOWS的缺省內(nèi)碼還是GBK。
由于編碼方案太多且彼此之間不兼容,,存在互相之間存在沖突的情況,,即對于同一個編碼數(shù)值,在兩種不同的編碼方案中代表的是兩個不同的字符,。這樣對于一些WEB應(yīng)用來說,,由于多種語言文字的同時使用及存儲,需要采用一種統(tǒng)一的字符集,。為此,,國際標(biāo)準(zhǔn)化組織(ISO)制定了ISO 10646碼表,,而Unicode協(xié)會制定了Unicode規(guī)范,這兩個體系剛開始時是獨(dú)立建立的,,在1991年,,雙方都認(rèn)識到世界不需要兩個不兼容的字符集。于是它們開始合并雙方的工作成果,,并為創(chuàng)立一個單一編碼表而協(xié)同工作,。從Unicode2.0開始,Unicode項目采用了與ISO 10646-1相同的字庫和字碼,。目前兩個項目仍都存在,,并獨(dú)立地公布各自的標(biāo)準(zhǔn)。Unicode協(xié)會現(xiàn)在的最新版本是2006年的Unicode 5.0,。
2,、ORACLE數(shù)據(jù)庫的字符集
ORACLE的字符集名字一般由以下部分組成:語言或區(qū)域、表示一個字符的比特位數(shù),、標(biāo)準(zhǔn)字符集名稱(可選項,,S或C,表示服務(wù)器或客戶端),。ORACLE字符集UTF8與UTFE不符合此規(guī)定,,其它基本都是這種格式。
set nls_lang=AMERICAN_AMERICA.UTF8
set nls_lang=SIMPLIFIED CHINESE_AMERICA.UTF8
對于US7ASCII,,表示區(qū)域是US,,用7個比特位表示一個字符,標(biāo)準(zhǔn)的字符集名稱為ASCII,。
對于中文字符集ZHS16GBK,,表示簡體中文(ZHT為繁體中文),一個字符需要16位比特,,標(biāo)準(zhǔn)的字符集名稱為GBK,。而ZHS16CGB231280表示簡體中文,一個字符需要16位比特,,標(biāo)準(zhǔn)的字符集名稱為GB231280,,屬于我們前面提過的1981年發(fā)布的GB2312-80標(biāo)準(zhǔn)。雖然我們說,,GBK編碼標(biāo)準(zhǔn)是GB2312編碼標(biāo)準(zhǔn)的擴(kuò)展,,但是數(shù)據(jù)庫字符集ZHS16GBK與ZHS16CGB231280之間卻不是嚴(yán)格的超集與子集的關(guān)系,主要是有些漢字的編碼在兩個字符集中的數(shù)值是不同的,,因此它們進(jìn)行字符集轉(zhuǎn)換時會出現(xiàn)問題,。
在本文中,有時候使用的是標(biāo)準(zhǔn)字符集名稱,,有時候又需要使用ORACLE字符集的名稱,,因此希望大家明白兩者之間的對應(yīng)關(guān)系,。
ORACLE數(shù)據(jù)庫有國家字符集(national character set)與數(shù)據(jù)庫字符集(database character set)之分。兩者都是在創(chuàng)建數(shù)據(jù)庫時需要設(shè)置的,。國家字符集主要是用于NCHAR,、NVARCHAR、NCLOB類型的字段數(shù)據(jù),,而數(shù)據(jù)庫字符集使用很廣泛,,它用于:CHAR、VARCHAR,、CLOB,、LONG類型的字段數(shù)據(jù);表名,、列名,、PL/SQL中的變量名,;輸入及保存在數(shù)據(jù)庫的SQL和PL/SQL的源碼,。
ORACLE支持的Unicode字符集有以下幾種,下面的列表給出了字符集的名稱,、對應(yīng)的數(shù)據(jù)庫版本范圍,、采用的Unicode的版本。
AL24UTFFSS:是ORACLE第一種支持Unicode的字符集,,從7.2版本開始使用,,但是它支持的Unicode版本為1.1,因此從9i開始就不支持此字符集了,。
UTF8:是ORACLE從ORACLE8開始使用的屬于UTF-8編碼的字符集,,從ORACLE8.0到ORACLE8.16,Unicode版本為2.1,,而ORACLE817到10g,,采用的Unicode標(biāo)準(zhǔn)為3.0
UTFE:用于EBCDIC碼平臺上的數(shù)據(jù)庫Unicode字符集。因此它屬于專用系統(tǒng)使用的字符集,,其它屬性與UTF8基本相同,。
AL32UTF8:是從ORACLE9開始使用的屬于UTF-8編碼的字符集,與UTF8相比,,它采用的Unicode版本更新,,在10g版本中使用的是Unicode 4.01標(biāo)準(zhǔn),而UTF8因?yàn)榧嫒菪缘目紤],,在10g版本中用的是Unicode 3.0標(biāo)準(zhǔn),。
AL16UTF16:是ORACLE第一種采用UTF-16編碼方式的字符集,從ORACLE9開始使用,,是作為缺省的國家字符集使用,,它不能被用作數(shù)據(jù)庫的字符集,。這是因?yàn)閿?shù)據(jù)庫的字符集決定了SQL與PL/SQL源碼的編碼方式,對于UTF-16這種使用固定的兩個字節(jié)來表示英文字母的編碼方案來說,,確實(shí)不適于用作數(shù)據(jù)庫的字符集,,ORACLE目前采用的數(shù)據(jù)庫字符集都是基于ASCII或EBCDID作為子集的編碼方案。
3,、如何選擇合適的數(shù)據(jù)庫字符集
3.1,、數(shù)據(jù)庫需要存儲的數(shù)據(jù)類型是字符集選擇的首要考慮目標(biāo)。
對于只存儲英文信息的數(shù)據(jù)庫等來說,,一般采用US7ASCII或WE8ISO8859P1等單字節(jié)的字符集就比較合適,,在性能和空間上也是最優(yōu),
同樣,,存儲了中文信息的數(shù)據(jù)庫,,如果采用單字節(jié)的字符集,也是不合適的,。在這種情況下,,數(shù)據(jù)庫的字符集雖然是US7ASCII或WE8ISO8859P1編碼,但里面存儲的數(shù)據(jù)編碼實(shí)際上卻是另外的編碼格式,,這種不一致的情況很容易引起問題,,建議不要這樣使用。ORACLE提供了很多種類的字符集供客戶選擇,,就是要滿足各種文字不同的編碼需要,。
3.2、字符集的選擇需要優(yōu)先考慮應(yīng)用程序的需要,。
目前出于國際化的需要,,軟件需要可以對不同的語言文字進(jìn)行處理,尤其一個系統(tǒng)中需要容納多種語言文字的時候,,一般都會采用Unicode這樣的通用解決方案,,即使會有一些空間和運(yùn)行效率的損失也是值得的。此時數(shù)據(jù)庫字符集建議可以采用AL32UTF8或UTF8編碼,,一種比較理想的模式就是由程序負(fù)責(zé)編碼格式的轉(zhuǎn)換,,而數(shù)據(jù)庫只提供一個透明的數(shù)據(jù)存儲,
客戶在應(yīng)用程序中輸入數(shù)據(jù),,此時數(shù)據(jù)的編碼格式是由客戶操作系統(tǒng)的區(qū)域及語言設(shè)置決定的,,如在簡體中文XP的環(huán)境下,輸入的中文編碼屬于GBK編碼,。在客戶輸入結(jié)束后,,程序首先判斷客戶的本地環(huán)境,并把編碼轉(zhuǎn)換成UNICODE,并通過NET傳送到服務(wù)器端,。由于客戶端與服務(wù)器數(shù)據(jù)庫的字符集均為UTF8格式,,ORACLE在傳送過程中不會進(jìn)行字符轉(zhuǎn)換,直接把數(shù)據(jù)按UTF8格式存儲到數(shù)據(jù)庫中,。查詢時是一個反向的過程,,應(yīng)用程序從數(shù)據(jù)庫中取出UTF8編碼的數(shù)據(jù),再由應(yīng)用程序根據(jù)客戶的本地環(huán)境,,把UTF8編碼的數(shù)據(jù)轉(zhuǎn)換成客戶本地的編碼格式,,最后把結(jié)果數(shù)據(jù)顯示給客戶。此方案的關(guān)鍵在于應(yīng)用程序要能很好的支持UNICODE編碼,,編碼的轉(zhuǎn)換由應(yīng)用程序來負(fù)責(zé),,數(shù)據(jù)庫只是提供了一個數(shù)據(jù)存儲功能。
對于部分程序來說,,由于對UNICODE支持不夠,,沒有提供編碼的轉(zhuǎn)換功能,則可以使用ORACLE提供的字符集轉(zhuǎn)換功能來實(shí)現(xiàn)同樣的目的,??蛻粼趹?yīng)用程序中輸入數(shù)據(jù),此時數(shù)據(jù)的編碼格式是由客戶操作系統(tǒng)的區(qū)域及語言設(shè)置決定的,,如在簡體中文XP的環(huán)境下,,輸入的中文編碼屬于GBK編碼,。在客戶輸入結(jié)束后,,程序直接把數(shù)據(jù)并通過NET傳送到服務(wù)器端。由于客戶端與服務(wù)器數(shù)據(jù)庫的字符集不一致,,因此ORACLE會把客戶端的編碼轉(zhuǎn)換成UTF8格式,,再把數(shù)據(jù)按UTF8格式存儲到數(shù)據(jù)庫中。這種方案的優(yōu)點(diǎn)就是程序可以不用支持UNICODE,,由ORACLE數(shù)據(jù)庫自動進(jìn)行轉(zhuǎn)換,。由于數(shù)據(jù)庫的字符集為UTF8,是其它字符集的超集,,因此在轉(zhuǎn)換過程中不會發(fā)生數(shù)據(jù)丟失的情況,。對于英文的字符符號,在UTF8中使用單字節(jié)存儲,,轉(zhuǎn)換的工作量很小,,可以忽略,而對于一些亞洲字符集,,在UTF8中一般需要兩到三個字節(jié)存儲,,需要的數(shù)據(jù)庫空間增加,而且轉(zhuǎn)換的工作量也相對大一些,性能會有一些損失,。
4,、與字符集相關(guān)的問題分析
4.1、在UTF8環(huán)境下運(yùn)行SQL語句報錯的問題:
SQL*PLUS工具不提供編碼自動轉(zhuǎn)換的功能,,當(dāng)數(shù)據(jù)庫字符集為UTF8,,客戶端的NLS_LANG如果也是UTF8,那么在SQL*PLUS中運(yùn)行SQL語句時,,語句全是英文,,不會出現(xiàn)問題,如果語句包含了中文或其它一些特殊字符,,SQL語句運(yùn)行時就會報錯,。對于返回的含中文的結(jié)果,SQL*PLUS也會顯示亂碼,。造成此錯誤的原因在于當(dāng)SQL語句中包含漢字等一些特殊字符時,,由于這些字符的編碼屬于GBK,ORACLE沒有進(jìn)行字符轉(zhuǎn)換,,而是直接把SQL語句送到服務(wù)器上進(jìn)行解析,。此時服務(wù)器的字符集是UTF8,因此它按UTF8編碼格式對SQL語句中GBK編碼的字符解析時就會產(chǎn)生錯誤,。如果把客戶端的NLS_LANG設(shè)置為本地環(huán)境的字符集,,如ZHS16GBK,此時可以直接在SQL*PLUS中輸入包含中文的SQL語句,,ORACLE在把SQL語句提交到服務(wù)器時會自動轉(zhuǎn)換成UTF8編碼格式,,因此SQL語句可以正常運(yùn)行。對于英文字母,,由于它在UTF8中的編碼數(shù)值采用的還是ASCII的編碼數(shù)值,,因此英文字母可以直接使用而不需要轉(zhuǎn)換,這就是如果SQL語句或輸出結(jié)果全是英文時不會出現(xiàn)錯誤的原因,。正確的做法是先把需要運(yùn)行的SQL做成腳本文件,,用代碼轉(zhuǎn)換工具把它轉(zhuǎn)換成UTF8編碼格式的文件,(注意,!XP中的記事本是提供了代碼轉(zhuǎn)換功能的,,可以在保存文件或選擇文件另存為的時候,彈出的對話框最后一項,,編碼,,選擇UTF8,再保存,,即可把文件轉(zhuǎn)換成UTF8編碼格式),。完成后用IE打開這個腳本,,選擇編碼-》UTF8,觀察此時SQL腳本是否含有亂碼或“,?”符號,。如果沒有,說明編碼格式已經(jīng)是UTF8了,,此時在SQL*PLUS中運(yùn)行這個腳本就不會產(chǎn)生錯誤了,。運(yùn)行結(jié)束后,輸出的結(jié)果中如果包含中文,,需要把結(jié)果SPOOL輸出到一個文件中,,然后用代碼轉(zhuǎn)換工具把這個結(jié)果文件由UTF8轉(zhuǎn)換成本地編碼格式,再用寫字板打開,,才能看到正常顯示的漢字,。由于IE具有代碼轉(zhuǎn)換功能,因此也可以不用代碼轉(zhuǎn)換工具,,直接在IE中打開輸出的結(jié)果文件,,選擇UTF8編碼,也能正常顯示含中文的結(jié)果文件,。
4.2,、數(shù)據(jù)庫出現(xiàn)亂碼的問題:
數(shù)據(jù)庫出現(xiàn)亂碼的問題主要和客戶的本地化環(huán)境,客戶端NLS_LANG設(shè)置,,服務(wù)器端的數(shù)據(jù)庫字符集設(shè)置這三者有關(guān),,如果它們的設(shè)置不一致或者某個設(shè)置錯誤,就會很容易出現(xiàn)亂碼,,下面我們簡要介紹以下幾種情況:
4.2.1,、數(shù)據(jù)庫字符集設(shè)置不當(dāng)引起的亂碼:
例如:一個存儲簡體中文字符的數(shù)據(jù)庫,它的字符集選用了US7ASCII,,當(dāng)它的客戶端NLS_LANG也選用US7ASCII時,,這個系統(tǒng)單獨(dú)使用是沒有問題的,因?yàn)閮烧咴O(shè)置一致,,因此ORACLE不會進(jìn)行字符集的轉(zhuǎn)換,客戶輸入的GBK碼被直接在數(shù)據(jù)庫中存儲起來,,當(dāng)查詢數(shù)據(jù)時,,實(shí)際客戶端取出來的數(shù)據(jù)也是GBK的編碼,因此顯示也是正常的,。但當(dāng)其它的系統(tǒng)需要從這個數(shù)據(jù)庫取數(shù)據(jù),,或者它的數(shù)據(jù)要EXP出來,IMP到其它數(shù)據(jù)庫時,,問題就會開始出現(xiàn)了,。其它系統(tǒng)的字符集一般是ZHS16GBK,或者其它系統(tǒng)客戶端的NLS_LANG設(shè)置為ZHS16GBK,此時必然會產(chǎn)生字符集的轉(zhuǎn)換,。雖然數(shù)據(jù)庫字符集設(shè)置為US7ASCII,,但我們知道,實(shí)際存儲的數(shù)據(jù)編碼是ZHS16GBK的,??上RACLE不會知道,它會把存儲的ZHS16GBK編碼數(shù)據(jù)當(dāng)作US7ASCII編碼的數(shù)據(jù),,按照US7ASCII轉(zhuǎn)換成ZHS16GBK的轉(zhuǎn)換算法進(jìn)行轉(zhuǎn)換,,可以想象,這種情況下,,亂碼的產(chǎn)生是必然的,。
4.2.2、數(shù)據(jù)庫字符集與客戶端NLS_LANG設(shè)置不同引起的亂碼:
例如:對于一個需要存儲簡體文信息的數(shù)據(jù)庫來說,,它的字符集設(shè)置和客戶端NLS_LANG設(shè)置一般可以使用ZHS16GBK編碼,。但是如果數(shù)據(jù)庫字符集選用了UTF8的話,也是可以的,,因?yàn)閆HS16GBK編碼屬于UTF8的子集,。ORACLE在數(shù)據(jù)庫與客戶端進(jìn)行數(shù)據(jù)交換時自動進(jìn)行編碼的轉(zhuǎn)換,在數(shù)據(jù)庫中實(shí)際存儲的也是UTF8編碼的數(shù)據(jù),。此時其它數(shù)據(jù)庫和此數(shù)據(jù)庫也可以正常的進(jìn)行數(shù)據(jù)交換,,因?yàn)镺RACLE會自動進(jìn)行數(shù)據(jù)的轉(zhuǎn)換。在實(shí)際使用中,,遇到過繁體XP的字符集ZHT16MSWIN950轉(zhuǎn)換成AL32UTF8字符集時,,一些特殊的字符和個別冷僻的漢字會變成亂碼。后來證實(shí)是XP需要安裝一個字庫補(bǔ)丁軟件,,最后順利解決此問題,。
4.2.3、客戶端NLS_LANG與本地化環(huán)境不同引起的亂碼:
一般情況下,,客戶端NLS_LANG與本地化環(huán)境采用了不同的字符集會出現(xiàn)亂碼,,除非本地化環(huán)境的字符集是客戶端NLS_LANG設(shè)置字符集的子集。如果把客戶端NLS_LANG設(shè)置為UTF8就屬于這種情況,,由于目前還沒有可以直接使用UNICODE字符集的操作系統(tǒng),,因此客戶本地化環(huán)境使用的字符集只能是某種語言支持的字符集,它屬于UTF8的子集,。下面我們就著重討論這種情況,。
雖然目前WINDOWS的內(nèi)核是支持UNICODE的,但是WINDOWS并不支持直接顯示UNICODE編碼的字符,而且它并不知道目前的字符采用了何種字符集,,所以默認(rèn)情況下,,它使用缺省的代碼頁來解釋字符,。因此,對于其它類型的編碼,,需要先進(jìn)行轉(zhuǎn)換,,變成系統(tǒng)目前的缺省代碼頁支持的字符集才能正常使用。
WINDOWS中的缺省代碼頁是由控制面板設(shè)置中的語言及區(qū)域的選擇所決定的,,屬于客戶本地化的環(huán)境設(shè)置,。簡體中文WINDOWS的字符編碼就是GBK,它的缺省代碼頁是936,。對于其它非WINDOWS的操作系統(tǒng),,我們可以把它們目前缺省使用的字符集作為用戶的本地化環(huán)境設(shè)置。另外,,我們使用的大部分工具,,如寫字板,SQL*PLUS等,,它們沒有提供編碼轉(zhuǎn)換功能,,因此在客戶端直接輸入或查詢數(shù)據(jù)往往都會遇到亂碼的問題,必須由應(yīng)用程序或一些工具去做編碼的轉(zhuǎn)換,,才能保證正常的使用,。
NLS_LANG 參數(shù)組成
NLS_LANG參數(shù)由以下部分組成:
NLS_LANG=<Language>_<Territory>.<Clients Characterset>
NLS_LANG各部分含義如下:
LANGUAGE指定:
-Oracle消息使用的語言
-日期中月份和日顯示
TERRITORY指定
-貨幣和數(shù)字格式
-地區(qū)和計算星期及日期的習(xí)慣
CHARACTERSET:
-控制客戶端應(yīng)用程序使用的字符集
通常設(shè)置或者等于客戶端(如Windows)代碼頁
或者對于unicode應(yīng)用設(shè)置為UTF8
在Windows上查看當(dāng)前系統(tǒng)的代碼頁可以使用chcp命令:
E:\>chcp
活動的代碼頁: 936
代碼頁936也就是中文字符集 GBK,在Microsoft的官方站點(diǎn)上,我們可以遭到關(guān)于936代碼頁的具體編碼規(guī)則,請參考以下鏈接:
http://www.microsoft.com/globaldev/reference/dbcs/936.htm
2. 查看 NLS_LANG 的方法
Windows使用:
echo %NLS_LANG%
如:
E:\>echo %NLS_LANG%
AMERICAN_AMERICA.ZHS16GBK
Unix使用:
env|grep NLS_LANG
如:
/opt/oracle>env|grep NLS_LANG
NLS_LANG=AMERICAN_CHINA.ZHS16GBK
Windows客戶端設(shè)置,可以在注冊表中更改NLS_LANG,具體鍵值位于:
HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOMExx\
xx指存在多個ORACLE_HOME時系統(tǒng)編號,。
3. 查看數(shù)據(jù)庫當(dāng)前字符集參數(shù)設(shè)置
SELECT * FROM v$nls_parameters;
4. 查看數(shù)據(jù)庫可用字符集參數(shù)設(shè)置
SELECT * FROM v$nls_valid_values;
5. 客戶端 NLS_LANG 的設(shè)置方法
Windows:
# 常用中文字符集
set NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
# 常用unicode字符集
set NLS_LANG=american_america.AL32UTF8
可以通過修改注冊表鍵值永久設(shè)置
HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOMExx\NLS_LANG
Unix:
# 常用unicode字符集
export NLS_LANG=american_america.AL32UTF8
# 常用中文字符集
export
可以編輯 bash_profile 文件進(jìn)行永久設(shè)置
vi .bash_profile
export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK"
or export NLS_LANG="Simplified Chinese_china".ZHS16GBK# 使 bash_profile 設(shè)置生效source .bash_profile