oracle字符集

轉自:http://www.cnblogs.com/morvenhuang/archive/2011/11/11/2245410.htmlhtml

ORACLE HANDBOOK系列之十:字符集、編碼以及Oracle的那些事

 

第一部分字符集與編碼常識
sql

字符集:數據庫

人們根據須要把某些字符收集到一處,並賦以名稱,因而便有了某某字符集。服務器

編碼:網絡

當前面收集的工做完成之後,爲了讓只認識數字的「愚蠢」的計算機也可以存儲字符,人們不得不爲集合裏的每個字符分配」身份證號碼」,這就是編碼,今後,終於能夠以存儲編碼的方式在計算機中存儲字符了。oracle

在字符集與編碼世界的漫漫歷史長河裏(僞),出現過若干個讓計算機工做者們如雷貫耳的名字,這些名字,有些已經成了浮雲飄散了,有些還在咱們的代碼中折騰。ide

ASCII函數

ü ASCII字符集:包含大小寫英文、阿拉伯數字、標點,以及一些不可見的控制符共128個。工具

ü ASCII編碼:使用7位表示一個字符。編碼範圍是[0-127](即Hex[00-7F]),其中[0-31](Hex[00-1F])部分以及127(Hex7F)是控制符,其他的都是些可見字符。post

GB2312:

ü GB2312字符集:ASCII字符集+7000左右漢字字符。

ü GB2312編碼:兼容ASCII編碼。對字節進行判斷,如值<=127,則意義等同於ASCII編碼;如值>127,則它須要跟其後的另外一個字節合併表示一個字符。其理論漢字編碼空間爲128X256,超過3萬個字符。

GBK

ü GBK字符集:GB2312字符集+20000左右漢字字符。

ü GBK編碼:兼容GB2312編碼。利用了GB2312編碼閒置的編碼空間。

GB18030:

ü GB18030字符集:GBK字符集+若干漢字+若干少數民族字符,爲目前國內最新的字符集。

ü GB18030編碼:兼容GBK編碼。繼續利用GBK編碼閒置的編碼空間,對於超出編碼空間的則採用4個字節表示。

BIG5

ü BIG5字符集:ASCII字符集+13000左右漢字(繁體)。

ü BIG編碼:兼容ASCII編碼。其編碼模式相似於GB2312.

UNICODE:(UNICODE一詞在平常使用中顯得寬泛、混亂,在不一樣的語境中能夠是如下意思之一。)

ü UNICODE標準:由一些組織提出的一套標準,對人類文字的顯示、編碼等進行了一系列的規定。

ü UNICODE字符集:目前最新版的UNICODE字符集中已經包含各類語言的超過10萬的字符。

ü UNICODE編碼:(狹義的UNICODE編碼可能指UCS-2,也可能指UTF-16;廣義的UNICODE編碼能夠指包括如下四種在內的若干種對UNICODE標準的編碼實現。)

1.         UTF-32編碼:固定使用4個字節來表示一個字符,存在空間利用效率的問題。

2.         UTF-16編碼:對相對經常使用的60000餘個字符使用兩個字節進行編碼,其他的(即’補充字符supplementary characters’)使用4字節。

3.         UCS-2編碼:是對UNICODE早期版本的實現,它與UTF-16的惟一區別是它不包括’補充字符’,因此它對字符的編碼只使用兩個字節。目前此編碼模式已過期。

4.         UTF-8編碼:兼容ASCII編碼;拉丁文、希臘文等使用兩個字節;包括漢字在內的其它經常使用字符使用三個字節;剩下的極少使用的字符使用四個字節。

ISO8859-1:(使用Oracle的同志們可能見過這個WE8ISO89859P1,沒錯,就是它。)

ü ISO8859-1字符集:ASCII字符集+若干西歐字符,例如字母Â、Ë。

ü ISO8859-1編碼:使用8位表示一個字符,同時移除了原ASCII編碼中的控制符(即[0-31],及127)。

Code page:(能夠把」code page」認爲是」編碼」的近義詞。至於爲何有這個名稱?歷史遺留問題。)

ü ANSI code pages:你必定見過ANSI,想一想另存文本文件時。ANSI code pages其實是一系列的編碼集合,根據操做系統區域設置而激活其中一種做爲默認ANSI編碼。例如公司電腦(英文系統)上的ANSI code page多是1252,而家裏的中文系統則多是936。因此在家裏能夠用ANSI存儲一個包含中文的文本文件,在公司則不行。能夠在註冊表鍵:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\NLS\CodePage\ACP中查看到當前使用的ANSI code page。 C#能夠經過Encoding.Default查看。

ü OEM code pages: OEM code pages是給控制檯應用程序(如SQLPLUS)使用的。除CJK環境(Chinese-Japanese-Korean)外,Windows使用不一樣的ANSI code page和OEM code page。例如,公司英文系統上使用的是437。可使用CHCP命令查看當前使用的OEM code page, C#能夠經過Console.OutputEncoding查看。

Code page 1252

ü cp1252字符集:ASCII字符集+若干西歐字符+若干特殊符號,好比™、‰.

ü cp1252編碼:使用8位表示一個字符。編碼範圍是[0-255](即Hex[00-FF]),[0-127]部分與ASCII相同,新增的大部分是西歐的字符,例如一些帶上標的字母Â、Ë,以及像這樣一類特殊符號)


PS1
:現實中兩臺PC上的code page信息

PC 1:英文版Windows XP,ANSI code page=1252, OEM code page=437

PC 2:中文版Windows 7,ANSI code page=936,   OEM code page=936

PS2:cp1252與cp437編碼表下載請猛擊這裏,早期控制檯應用程序經常須要畫一些粗糙的表格等等圖形,因此能夠在437中看到很多不一樣的橫線豎線這一類的特殊符號。

PS3:CP125二、ISO8859-一、ASCII比較,就實際使用的編碼範圍來講:CP1252>ISO8859-1>ASCII。ASCII是[0-127],CP1252是[0-255],ISO8859-1則移除了cp1252中[0-31]及127這些不可見的控制符,同進移除了[128-159](即Hex[80-9F])中的特殊符號。

 

第二部分 Oracle中的編碼與字符集

1.爲何須要兩個字符集?

Oracle中有兩個字符集:

1)數據庫字符集

2)國家字符集

爲何要有兩個字符集?若是我知道只須要英文,設置數據庫字符集=US7ASCII,若是我知道只須要西歐字符,設置數據庫字符集=WE8MSWIN1252或者WE8ISO89859P1,或者乾脆就用AL32UTF8。你看,我只須要設定「數據庫字符集」,那麼「國家字符集」有什麼必要呢?

其實,考慮到歷史遺留問題以及數據庫建立者們沒法避免的「短視」,不少現有數據庫都沒法支持UNICODE字符集,例如要在現有的US7ASCII數據庫字符集的數據庫中存儲中文,這個時候「國家字符集」+NVARCHAR2這樣的組合就能救你一命了。對於數據類型爲NVARCHAR2(以及NCHAR, NCLOB)的字段,它使用是國家字符集,與數據庫字符集的設置無關。自9i之後,國家字符集可選的只有AL16UTF16與AL32UTF8,UTF-16與UTF-8都是UNICODE編碼標準的實現,因些能夠表示世界上幾乎全部的文字。

固然,若是數據庫字符集自己就使了UNICODE字符集,就沒有必要使用NVARCHAR2, NCHAR, NCLOB這些類型了。

2.字符集名稱的玄機

Oracle對字符集的命名實際上有必定的規則可尋,例如:

AL32UTF8

【AL】支持全部語言(All Language)。

【32】每字符最多佔用32位(4字節)。

【UTF8】編碼爲UTF-8。

WE8MSWIN1252

【WE】支持西歐語言(Western Europe)。

【8】每字符須要佔用8位(單字節)。

【MSWIN1252】編碼爲CP1252。

US7ASCII

【US】表示美國(United States)。

【7】每字符須要佔用7位。

【ASCII】編碼爲ASCII。

其它如ZHS16GBK,ZHT16BIG5,US8PC437(編碼爲OEM cp437),均可以類推。

3.例子很重要

3.1.準備兩個數據庫

上帝說要有例子,因而有了兩個相同版本的數據庫,A跟B:

複製代碼
SELECT parameter, VALUE
FROM nls_database_parameters
WHERE parameter IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET')

--數據庫A:
PARAMETER                      VALUE
------------------------------ -------------------
NLS_CHARACTERSET               WE8MSWIN1252
NLS_NCHAR_CHARACTERSET         AL16UTF16

--數據庫B:
PARAMETER                      VALUE
------------------------------ ----------------- 
NLS_CHARACTERSET               AL32UTF8
NLS_NCHAR_CHARACTERSET         AL16UTF16

--在A和B中分別建立一張表。
CREATE TABLE charset_test 
(id NUMBER(4) PRIMARY KEY,
vc VARCHAR2(20),
nvc NVARCHAR2(20));
複製代碼


3.2.
工具很重要

在測試以前,爲避免工具自己的特性給人形成的困惑,介紹一下幾個客戶端工具對UNICODE 的支持狀況:

ü SQLPLUS:不支持UNICODE字符集。是否支持中文取決於當前的OEM code page,若是是cp437,不管輸入仍是顯示中文都是不可能的。但若是是cp936,則能夠支持中文輸入輸出。

ü PLSQL Developer:7.0版本的查詢結果窗口支持UNICODE字符集,可是編輯窗口(即輸入SQL語句的窗口)不支持。8.0版徹底支持UNICODE。

ü Oracle SQL Developer:查詢結果窗口與編輯窗口都支持UNICODE字符集。


3.3.
出現亂碼了

這裏使用Oracle SQL Developer,分別在A、B中插入並查詢中文:

View Code

暫時先跳過VARCHAR2字段,先來關注NVARCHAR2字段,爲何在A庫不能正常顯示?無非有這幾種可能:

ü 客戶端操做系統不支持顯示中文。

ü Oracle客戶端工具(這裏是Oracle SQL Developer)不支持顯示中文。

ü Oracle客戶端有相關設置(好比NLS_LANG)不正確。

ü 存儲在數據庫中的數據已是不正確的數據。

第一點,客戶端操做系統是否支持中文對運行於其上的應用程序有影響嗎?應該有兩種狀況,一種是應用程序依賴於操做系統的中文支持;另外一種是有一些軟件本身帶有語言包及字體(好比Adobe的一些產品,.NET程序在編譯的時候也能夠選擇將字體文件打包進去),那麼它應該不依賴於操做系統。

我猜想Oracle SQL Developer應該是屬於前一種,同時我檢查了操做系統,肯定其已經支持東亞語言(Control panel—Regional and language options—Language tab—Supplemental languages support—Install files for East Asian languages,若是checkbox已經選中,說明已經安裝東亞語言包)。

第二點,不管查詢結果窗口仍是編輯窗口都支持UNICODE字符集。

第三點,因爲不依賴於Oracle client的OCI,客戶端註冊表中的NLS_LANG設置對像Oracle SQL Developer沒有影響。

第四點,咱們藉助DUMP()函數來肯定NVARCHAR2字段中具體的內容。

DUMP()的語法:DUMP(<value>[,<format>[,<offset>[,<length>]]])

其中的format參數:若是是8則表示結果使用8進製表示,若是是16則表示16進制,若是是0到16間的其它數則都使用10進制。若是是大於16的數,則分幾種狀況:若是是可見的ASCII字符則直接打印此字符,若是是控制字符則打印成「^x」,其它狀況則把結果按16進制顯示。爲format加上1000則表示除了輸出結果以外,還會附帶輸出所使用的字符集信息。

 這裏咱們使用:

View Code

咱們知道「中」字的UTF-16編碼是4E2D,顯然在A庫中存儲的數據已是不對的,00BF實際上就是一個倒的問號字符,其存儲在數據庫中的原始數據已經不對了,更況且是客戶端的顯示。


3.4.
找不一樣

那麼爲何兩個庫會不同呢?嫌疑很快就落在了數據庫字符集上,由於A和B的區別只在數據庫字符集上,一個是WE8MSWIN1252,另外一個是AL32UTF8。通過測試,結論是:

Oracle SQL Developer忽略NLS_LANG,字符串直接以照數據庫字符集進行編碼後由客戶端傳輸到服務器端。因爲A庫數據庫字符集不支持漢字,很不幸地被替換成了默認的BF並最終被存儲到數據庫中,永遠地錯下去。B庫則相反,中文在傳輸的過程當中「存活」下來併成功到達服務器端,最終自動轉換成NVARCHAR2所用的編碼並存儲到庫中。


3.5.
如何讓NVARCHAR2字段工做

看起來彷佛A庫中的NVARCHAR2字段永遠也沒法正常使用了,並不是這樣,對於Oracle SQL Developer,經過一些設置,就可讓NVARCHAR2能夠正常地插入、查詢。

找到{ORACLE_HOME}\sqldeveloper\sqldeveloper\bin\sqldeveloper.conf(依賴於你的Oracle SQL Developer安裝路徑),添加一行配置:

AddVMOption -Doracle.jdbc.convertNcharLiterals=true

同時在中文字符串前添加「N」前綴:

View Code

這個配置起到的做用是這樣的:在INSERT語句從客戶端傳輸到服務器端以前,Oracle SQL Developer檢測(其實是JDBC檢測)語句,若是發現「N」前綴,則事先將這部分的字符串按UTF-16進行編碼獲得16進制串。也就是至關於執行了這個命令:

INSERT INTO charset_test VALUES(2,’中’,UNISTR('\4e2d'));

C#不須要作特殊的配置來讓NVARCHAR2正常工做,只須要在執行INSERT時使用參數並選擇正確的參數類型選:

View Code


4.
客戶端的NLS_LANG設置及編碼轉換

前面我說過Oracle SQL Developer忽略客戶端NLS_LANG設置,那麼對於其它的工具呢?(這裏咱們主要關注字符集及編碼,不討論NLS_LANG對日期格式、排序方式、數字顯示格式等等的影響)

ü SQLPLUS,插入與查詢都依賴於客戶端NLS_LANG設置。一般,客戶端NLS_LANG設置要與當前的OEM Codepage一致,好比US8PC437。

ü PL/SQL Developer插入與查詢都依賴於客戶端NLS_LANG設置。一般,客戶端NLS_LANG設置要與數據庫字符集一致。

使用SQLPLUS能夠清晰地看到Oracle編碼轉換的過程:

1) 在Oracle客戶端向服務器端提交SQL語句時,Oracle客戶端根據NLS_LANG和數據庫字符集,對從應用程序接傳送過來的字符串編碼進行轉換處理。若是NLS_LANG與數據庫字符集相同,不做轉換,不然要轉換成數據庫字符集並傳送到服務器。服務器在接收到字符串編碼以後,對於普通的CHAR或VARCHAR2類型,直接存儲;對於NCHAR或NVARCHAR2類型,服務器端將其轉換爲國家字符集再存儲。

2) 在查詢數據時,服務器端原樣返回存儲在庫中的數據,由客戶端根據返回的元數據中的字符集信息與NLS_LANG和NLS_NCHAR的設置進行比較(若是NLS_NCHAR沒有設置,則其默認值爲NLS_LANG中的字符集設置),若是元數據中的字符集信息與客戶端設置一致,不進行轉換,不然要進行轉換。國家字符集的轉換根據NLS_NCHAR設置進行轉換。

這裏我也舉幾個使用SQLPLUS的測試例子,分別在A、B兩庫執行相同的語句,而後經過網絡抓包查看從Oracle client傳輸到服務器的具體內容。

1 客戶端NLS_LANG:WE8MSWIN1252

SQL命令:insert into charset_test values(1,'æ',null);

網絡抓包(A庫,數據庫字符集爲WE8MSWIN1252):91

解釋:因爲應用程序(即SQLPLUS)使用的編碼是Codepage437,因此æ的編碼是91。當91被傳給Oracle client後,Oracle client根據NLS_LANG誤判其使用的編碼是Codepage1252,又因爲NLS_LANG設置與數據庫字符集一致,因而Oracle client不進行編碼轉換,91被直接傳給服務器並存儲,考慮到數據庫字符集是Codepage1252,很顯然91是錯誤的數據(字符[æ]在Codepage1252下的編碼是E6,而非91)。

這個錯誤致使了一個有趣的現象,那就是在同一個客戶端使用SQLPLUS查詢竟然能夠看到正確字符[æ],這是因爲SELECT的時候91也被直接返回,而且在Oracle client也不進行編碼轉換而是直接傳給了應用程序,恰巧應用程序根據本身使用的編碼能夠正確解析91。可是換一個客戶端機器,或者換一個客戶端工具均可能獲得不同的查詢結果。

網絡抓包(B庫,數據庫字符集爲AL32UTF8):E2 80 98

解釋:因爲應用程序(即SQLPLUS)使用的編碼是Codepage437,因此æ的編碼是91。當91被傳給Oracle client後,Oracle client根據NLS_LANG誤判其使用的編碼是Codepage1252,而91在Codepage1252中對應的是字符[‘],根據Codepage1252到數據字符集UTF8的轉換,最終轉換成了E2 80 98,即UTF8下的[‘]。

2客戶端NLS_LANG:US7ASCII

SQL命令:insert into charset_test values(1,'æ',null);

網絡抓包(A庫):BF

解釋:因爲應用程序(即SQLPLUS)使用的編碼是Codepage437,因此æ的編碼是91。當91被傳給Oracle client後,Oracle client根據NLS_LANG誤判其使用的編碼是ASCII,而91在ASCII中是無效編碼,根據ASCII到數據字符集Codepage1252的轉換,最終轉換成了BF,BF是Codepage1252遇到無效編碼時使用的默認替換編碼。

網絡抓包(B庫): EF BF BD

解釋:因爲應用程序(即SQLPLUS)使用的編碼是Codepage437,因此æ的編碼是91。當91被傳給Oracle client後,Oracle client根據NLS_LANG誤判其使用的編碼是ASCII,而91在ASCII中是無效編碼,根據ASCII到數據字符集UTF8的轉換,最終轉換成了EF BF BD,EF BF BD是UTF8遇到無效編碼時使用的默認替換編碼。

3客戶端NLS_LANG:US8PC437

SQL命令:insert into charset_test values(1,'æ',null);

網絡抓包(A庫):E6

解釋:E6是字符[æ]的正確的Codepage1252編碼,這次因爲應用程序(即SQLPLUS)使用的是Codepage437,Oracle client從NLS_LANG得到的編碼信息也是Codepage437,因而進行了正確的編碼轉換。

網絡抓包(B庫):C3 A6 

解釋:C3 A6是字符[æ]的正確的UTF8編碼,這次因爲應用程序(即SQLPLUS)使用的是Codepage437,Oracle client從NLS_LANG得到的編碼信息也是Codepage437,因而進行了正確的編碼轉換。

我以爲,只有SQLPLUS的日子老是那麼美好,一切看起來既合理又可解釋。當其它工具出現以後,世界就變得一團亂麻了,Oracle SQL Developer徹底忽略客戶端NLS_LANG設置卻是讓事情變得簡單,不過PL/SQL Developer則是另外一回事,我花了4天時間企圖搞明白其中的編碼轉換過程,最終只證實它就是個不可理喻的玩意兒,惟一目前看起來還正確的結論是:若是要用PL/SQL Developer,只好仍是把NLS_LANG設置得跟數據庫字符集一致。其它就只能自求多福了。


5.NLS_LANG
ODP.NET的影響

View Code

惟一受客戶端NLS_LANG影響的是OracleString的GetNonUnicodeBytes()方法,此方法依賴於客戶端本地設置的字符集,例如咱們把NLS_LANG從AMERICAN_AMERICA.WE8MSWIN1252改爲AMERICAN_AMERICA.US7ASCII

其中230(即HexE6)正是字符‘æ’的編碼,而63(即Hex3F)是ASCII中的問號(因爲ASCII字符集中沒有‘æ’,故用問號代替)。


6.關於
VARCHAR2, NVARCHAR2的其它問題

NVARCHAR2(N),其中的N是指字符數,不是字節數。不過其最大長度是以字節爲單位,即4000字節。

VARCHAR2(N),其中的N多是指字符數,也多是指字節數。你能夠顯式地在聲明的時候指定,好比VARCHAR2(10 BYTE)或者VARCHAR2(10 CHAR),未顯式指明時,則由參數NLS_LENGTH_SEMANTICS決定。須要注意的是你能成功聲明VARCHAR2(4000 CHAR)並不能保證你能真的存儲4000個字符,若是超過4000字節,該報錯Oracle仍是會報錯。

 

【參考及引用】:

1. http://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html

2. http://www.laoxiong.net/category/oracle/orainternal/page/2

3. http://en.wikipedia.org/wiki/Windows-1252

4. http://en.wikipedia.org/wiki/ASCII

5. http://en.wikipedia.org/wiki/Code_page_936

6. http://en.wikipedia.org/wiki/Code_page_437

7. http://en.wikipedia.org/wiki/UTF-8

8. http://en.wikipedia.org/wiki/UTF-16/UCS-2

9. http://en.wikipedia.org/wiki/UTF-32/UCS-4

10. http://en.wikipedia.org/wiki/GB_18030

11. http://en.wikipedia.org/wiki/Unicode

12. OReilly Oracle PL SQL Programming 5th Edition,Steven Feuerstein, Bill Pribyl

13. http://www.laruence.com/2009/08/22/1059.html

14. http://en.wikipedia.org/wiki/Windows_code_page

相關文章
相關標籤/搜索