最后我們來(lái)討論一下亂碼的產(chǎn)生。
通常在我們的現(xiàn)實(shí)環(huán)境中,存在3個(gè)字符集設(shè)置。
第一: 客戶端應(yīng)用字符集(Client Application Character Set)
第二: 客戶端NLS_LANG參數(shù)設(shè)置
第三: 服務(wù)器端,數(shù)據(jù)庫(kù)字符集(Character Set)設(shè)置
我們說(shuō),一個(gè)字符在客戶端應(yīng)用(比如SQLPLUS,CMD,NOTEPAD等)中以怎樣的字符顯示取決于客戶端操作系統(tǒng),客戶端能夠顯示怎樣的字符,
我們就可以在應(yīng)用中錄入這些字符,至于這些字符能否在數(shù)據(jù)庫(kù)中正常存儲(chǔ),就和另外的兩個(gè)字符集設(shè)置緊密相關(guān)了。
在傳輸過(guò)程中,客戶端NLS_LANG主要用于進(jìn)行轉(zhuǎn)換判斷
如果NLS_LANG等于數(shù)據(jù)庫(kù)字符集,則不進(jìn)行任何轉(zhuǎn)換直接把字符插入數(shù)據(jù)庫(kù)
如果不同則進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換主要有兩個(gè)任務(wù)
- 如果存在對(duì)應(yīng)關(guān)系,則把相應(yīng)二進(jìn)制編碼經(jīng)過(guò)映射后(這一步映射以后,所代表的字符可能發(fā)生轉(zhuǎn)換)傳遞給數(shù)據(jù)庫(kù)
- 如果不存在對(duì)應(yīng)關(guān)系,則傳遞一個(gè)替換字符(很多平臺(tái)就是?)
數(shù)據(jù)庫(kù)字符集,在和客戶端NLS_LANG不同時(shí),會(huì)把經(jīng)過(guò)NLS_LANG轉(zhuǎn)換的字符進(jìn)行進(jìn)一步處理
- 對(duì)于?(即不存在對(duì)應(yīng)關(guān)系的字符)直接以?形式存放入數(shù)據(jù)庫(kù)
- 對(duì)于其他字符,在NLS_LANG和數(shù)據(jù)庫(kù)字符集之間進(jìn)行轉(zhuǎn)換后存入。
以下我們來(lái)看一下最為常見(jiàn)的字符集及亂碼的產(chǎn)生:
1.當(dāng)NLS_LANG字符集與數(shù)據(jù)庫(kù)字符集不同,同時(shí)NLS_LANG不同于Server端字符集設(shè)置
在這種情況下,存在兩種可能:
-
客戶端輸入的字符在NLS_LANG中沒(méi)有對(duì)應(yīng)的字符,這時(shí)無(wú)法轉(zhuǎn)換,NLS_LANG使用替換字符替代這些無(wú)法映射的字符(這一步轉(zhuǎn)換在TTS中
完成),在很多字符集中這個(gè)替代字符就是”?” -
當(dāng)客戶端的字符在NLS_LANG中對(duì)應(yīng)了不同的字符時(shí),傳遞給數(shù)據(jù)庫(kù)以后發(fā)生轉(zhuǎn)換,存儲(chǔ)的是字符,但是已經(jīng)丟失了元數(shù)據(jù),數(shù)據(jù)庫(kù)中
的字符不再代表客戶端的輸入。而且這個(gè)過(guò)程不可逆,這也就是為什么很多時(shí)候在客戶端輸入的是正常的編碼,查詢之后會(huì)得到未知字符的原因。
我們通過(guò)上圖來(lái)簡(jiǎn)單說(shuō)明一下這個(gè)過(guò)程,當(dāng)客戶端在WE8ISO8859P15字符集時(shí),輸入歐元符號(hào): €,這時(shí)客戶端NLS_LANG和數(shù)據(jù)庫(kù)端字符集不同,
進(jìn)行第一次轉(zhuǎn)換,客戶端€符號(hào)編碼是A4,在NLS_LANG轉(zhuǎn)換時(shí),A4對(duì)應(yīng)了NLS_LANG中的‘¤’,這一步的轉(zhuǎn)換產(chǎn)生了錯(cuò)誤映射。由于數(shù)據(jù)庫(kù)字符集不
同于NLS_LANG設(shè)置,這時(shí)進(jìn)一步的轉(zhuǎn)換發(fā)生了,存入數(shù)據(jù)庫(kù)的編碼變成了C2A4,雖然同NLS_LANG進(jìn)行了正確的轉(zhuǎn)換,但是客戶端錄入的數(shù)據(jù)已經(jīng)
損壞或者丟失了。
我們可以用我們熟悉的字符集做一個(gè)簡(jiǎn)單的測(cè)試:
測(cè)試環(huán)境:
客戶端應(yīng)用為中文18030字符集
NLS_LANG設(shè)置為US7ASCII字符集
數(shù)據(jù)庫(kù)CHARACTER SET為ZHS16GBK
c:\>set NLS_LANG=AMERICAN_AMERICA.US7ASCII c:\>sqlplus eygle/eygle SQL*Plus: Release 9.2.0.4.0 - Production on Tue Nov 4 01:19:57 2003 Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. Connected to: Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production With the Partitioning, Oracle Label Security, OLAP and Oracle Data Mining options JServer Release 9.2.0.4.0 - Production SQL> insert into test values('測(cè)試'); 1 row created. SQL> select name,dump(name) from test; NAME DUMP(NAME) -------------------------------------------------- 2bJT Typ=1 Len=4: 50,98,74,84 這時(shí)候我們發(fā)現(xiàn),查詢出來(lái)的是混亂的字符,我們把這些字符轉(zhuǎn)換為2進(jìn)制就是 110010 1100010 1001010 1010100 補(bǔ)全8位就是 00110010 01100010 01001010 01010100 我們把首位換成1 10110010 11100010 11001010 11010100 我們來(lái)看正確的存儲(chǔ): c:\>sqlplus eygle/eygle SQL*Plus: Release 9.2.0.4.0 - Production on Tue Nov 4 01:40:18 2003
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
SQL> insert into test values('測(cè)試'); 1 row created.
SQL> col dump(name) for a30
NAME DUMP(NAME)
1 row selected.
我們把這個(gè)結(jié)果轉(zhuǎn)換為2進(jìn)制表示 這個(gè)結(jié)果正是我們前面亂碼首位補(bǔ)全1后的結(jié)果。
這個(gè)測(cè)試說(shuō)明在US7ASCII轉(zhuǎn)換中文的時(shí)候除去了首位的 1,這樣就丟失了元數(shù)據(jù),導(dǎo)致亂碼出現(xiàn),NLS_LANG的轉(zhuǎn)換作用由此可加一斑!
|
3. NLS_LANG和數(shù)據(jù)庫(kù)字符集相同時(shí)
在這種情況下,數(shù)據(jù)庫(kù)端對(duì)客戶端傳遞過(guò)來(lái)的編碼不進(jìn)行任何轉(zhuǎn)換(這樣可以提高性能),直接存儲(chǔ)進(jìn)入數(shù)據(jù)庫(kù),那么這時(shí)候就存在和上面同樣的問(wèn)題,
如果客戶端傳遞過(guò)來(lái)的字符集在數(shù)據(jù)庫(kù)中有正確的對(duì)應(yīng)就可以正確存儲(chǔ),如果沒(méi)有,就會(huì)被替換字符置換成?,亂碼就這樣產(chǎn)生了。
如上圖所示,當(dāng)NLS_LANG和數(shù)據(jù)庫(kù)字符集設(shè)置相同都為UTF8時(shí),客戶端的歐元符號(hào)的編碼A4就不會(huì)經(jīng)過(guò)任何轉(zhuǎn)換就插入到數(shù)據(jù)庫(kù)中,而在UTF8的數(shù)
據(jù)庫(kù)中,A4代表的是一個(gè)非法字符。
我們來(lái)看一個(gè)簡(jiǎn)單的測(cè)試
測(cè)試環(huán)境:
客戶端字符集應(yīng)用為中文GB18030
客戶端NLS_LANG為US7ASCII
數(shù)據(jù)庫(kù)字符集為US7ASCII
我們知道這個(gè)時(shí)候,存入的數(shù)據(jù),數(shù)據(jù)庫(kù)不進(jìn)行任何轉(zhuǎn)換,在以下的測(cè)試中,我們看到中文在US7ASCII字符集下得以正確顯示。
c:\>set nls_lang=AMERICAN_AMERICA.US7ASCII c:\>sqlplus eygle/eygle SQL*Plus: Release 9.2.0.4.0 - Production on Tue Nov 4 01:02:04 2003 Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. Connected to: Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production With the Partitioning, Oracle Label Security, OLAP and Oracle Data Mining options JServer Release 9.2.0.4.0 - Production SQL> insert into test values('測(cè)試'); 1 row created. SQL> commit; Commit complete. SQL> select * from test; NAME ---------- 測(cè)試 1 row selected. SQL> col dump(name) for a30 SQL> select name,dump(name) from test; NAME DUMP(NAME) ---------- ------------------------------ 測(cè)試 Typ=1 Len=4: 178,226,202,212 1 row selected. SQL> select * from nls_database_parameters; PARAMETER VALUE ------------------------------ ---------------------------------------- NLS_LANGUAGE AMERICAN NLS_TERRITORY AMERICA NLS_CURRENCY $ NLS_ISO_CURRENCY AMERICA NLS_NUMERIC_CHARACTERS ., NLS_CHARACTERSET US7ASCII NLS_CALENDAR GREGORIAN NLS_DATE_FORMAT DD-MON-RR NLS_DATE_LANGUAGE AMERICAN NLS_SORT BINARY NLS_TIME_FORMAT HH.MI.SSXFF AM PARAMETER VALUE ------------------------------ ---------------------------------------- NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR NLS_DUAL_CURRENCY $ NLS_COMP BINARY NLS_LENGTH_SEMANTICS BYTE NLS_NCHAR_CONV_EXCP FALSE NLS_NCHAR_CHARACTERSET AL16UTF16 NLS_RDBMS_VERSION 9.2.0.4.0 20 rows selected. SQL> |
結(jié)語(yǔ):
對(duì)于DBA來(lái)說(shuō),有一個(gè)很重要的原則就是:不要把你的數(shù)據(jù)庫(kù)置于危險(xiǎn)的境地!
這就要求我們,在進(jìn)行任何可能對(duì)數(shù)據(jù)庫(kù)結(jié)構(gòu)發(fā)生改變的操作之前,先做有效的備份,很多DBA沒(méi)有備份的操作中得到了慘痛的教訓(xùn)。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
