Oracle实例解析:编码与字符集

来源:互联网 发布:icp数据是什么意思 编辑:程序博客网 时间:2024/06/07 06:52
名人名言:正如每一条金镂是宝贵的,每一刻时间也是宝贵的。——梅森

字符集:人们按照须要把某些字符收集到一处,并赋以名称,于是便有了某某字符集。

编码:当前面收集的工作完成今后,为了让只熟悉数字的“笨拙”的策画机也可以或许存储字符,人们不得不为凑集里的每一个字符分派”身份证号码”,这就是编码,从此,终于可以以存储编码的体式格式在策画机中存储字符了。

在字符集与编码世界的漫漫汗青长河里(伪),呈现过若干个让策画机工作者们如雷贯耳的名字,这些名字,有些已经成了浮云飘散了,有些还在我们的代码中折腾。

ASCII:

ASCII字符集:包含大小写英文、阿拉伯数字、标点,以及一些不成见的把握符共128个。

ASCII编码:应用7位默示一个字符。编码局限是[0-127](即Hex[00-7F]),此中[0-31](Hex[00-1F])项目组以及127(Hex7F)是把握符,其余的都是些可见字符。

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标准的编码实现。)

①UTF-32编码:固定应用4个字节来默示一个字符,存在空间哄骗效力的题目。

② UTF-16编码:对相对常用的60000余个字符应用两个字节进行编码,其余的(即’补充字符supplementary characters’)应用4字节。

③UCS-2编码:是对UNICODE早期版本的实现,它与UTF-16的独一差别是它不包含’补充字符’,所以它对字符的编码只应用两个字节。今朝此编码模式已过期。

④ 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:CP1252、ISO8859-1、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中有两个字符集:数据库字符集和国度字符集。

为什么要有两个字符集?若是我知道只须要英文,设置数据库字符集=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)例子很首要

①筹办两个数据库

上帝说要有例子,于是有了两个雷同版本的数据库,A跟B:

SELECT parameter, VALUE
FROM nls_database_parameters
WHERE parameter IN (""NLS_CHARACTERSET"", ""NLS_NCHAR_CHARACTERSET"")
--数据库A:
PARAMETER&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; VALUE
------------------------------ -------------------
NLS_CHARACTERSET&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; WE8MSWIN1252
NLS_NCHAR_CHARACTERSET&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; AL16UTF16
--数据库B:
PARAMETER&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; VALUE
------------------------------ -----------------
NLS_CHARACTERSET&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; AL32UTF8
NLS_NCHAR_CHARACTERSET&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; AL16UTF16
--在A和B平分别创建一张表。
CREATE TABLE charset_test
(id NUMBER(4) PRIMARY KEY,
vc VARCHAR2(20),
nvc NVARCHAR2(20));

&#160; ②对象很首要

在测试之前,为避免对象本身的特点给人造成的困惑,介绍一下几个客户端对象对UNICODE 的支撑景象:

SQLPLUS:不支撑UNICODE字符集。是否支撑中文取决于当前的OEM code page,若是是cp437,无论输入还是显示中文都是不成能的。但若是是cp936,则可以支撑中文输入输出。

PLSQL Developer:7.0版本的查询成果窗口支撑UNICODE字符集,然则编辑窗口(即输入SQL语句的窗口)不支撑。8.0版完全支撑UNICODE。

Oracle SQL Developer:查询成果窗口与编辑窗口都支撑UNICODE字符集。

③呈现乱码了

这里应用Oracle SQL Developer,分别在A、B中插入并查询中文:

INSERT INTO charset_test VALUES(1,""中"",""中"");
COMMIT;
--A库
SELECT * FROM charset_test;
1&#160;&#160;&#160;&#160; ?&#160;&#160;&#160;&#160; ?
--B库
SELECT * FROM charset_test;
1&#160;&#160;&#160;&#160; 中&#160;&#160;&#160; 中

临时先跳过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([,[,[,]]])

此中的format参数:若是是8则默示成果应用8进制默示,若是是16则默示16进制,若是是0到16间的其它数则都应用10进制。若是是大于16的数,则分几种景象:若是是可见的ASCII字符则直接打印此字符,若是是把握字符则打印成“^x”,其它景象则把成果按16进制显示。为format加上1000则默示除了输出成果之外,还会附带输出所应用的字符集信息。

这里我们应用:

SELECT DUMP(nvc,1016) FROM charset_test;
--A库
Typ=1 Len=2 CharacterSet=AL16UTF16: 0,bf
--B库
Typ=1 Len=2 CharacterSet=AL16UTF16: 4e,2d

&#160; 我们知道“中”字的UTF-16编码是4E2D,显然在A库中存储的数据已经是不合错误的,00BF实际上就是一个倒的问号字符,其存储在数据库中的原始数据已经不合错误了,更何况是客户端的显示。

④找不合

那么为什么两个库会不一样呢?嫌疑很快就落在了数据库字符集上,因为A和B的差别只在数据库字符集上,一个是WE8MSWIN1252,另一个是AL32UTF8。经过测试,结论是:

Oracle SQL Developer忽视NLS_LANG,字符串直接以照数据库字符集进行编码后由客户端传输到办事器端。因为A库数据库字符集不支撑汉字,很不幸地被调换成了默认的BF并终极被存储到数据库中,永远地错下去。B库则相反,中文在传输的过程中“存活”下来并成功达到办事器端,终极主动转换成NVARCHAR2所用的编码并存储到库中。

⑤如何让NVARCHAR2字段工作

看起来似乎A库中的NVARCHAR2字段永远也无法正常应用了,并非如许,对于Oracle SQL Developer,经由过程一些设置,就可以让NVARCHAR2可以正常地插入、查询。

找到{ORACLE_HOME}\sqldeveloper\sqldeveloper\bin\sqldeveloper.conf(依附于你的Oracle SQL Developer安装路径),添加一行设备:

AddVMOption -Doracle.jdbc.convertNcharLiterals=true

同时在中文字符串前添加“N”前缀:

INSERT INTO charset_test VALUES(2,""中"",N""中"");
--NVARCHAR2列中的中文不再是乱码了
SELECT * FROM charset_test WHERE id=2;
2&#160;&#160;&#160;&#160; ?&#160;&#160;&#160;&#160; 中

&#160; 这个设备起到的感化是如许的:在INSERT语句从客户端传输到办事器端之前,Oracle SQL Developer检测(实际上是JDBC检测)语句,若是发明“N”前缀,则事先将这项目组的字符串按UTF-16进行编码获得16进制串。也就是相当于履行了这个号令:

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

C#不须要做特别的设备来让NVARCHAR2正常工作,只须要在履行INSERT时应用参数并选择正确的参数类型选:

cmd.CommandText = " into charset_test values(3,:vc,:nvc)";
OracleParameter p1 = new OracleParameter("vc", OracleDbType.Varchar2);
OracleParameter p2 = new OracleParameter("nvc", OracleDbType.NVarchar2);
p1.Value = "中";
p2.Value = "中";
cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
cmd.uteNonQuery();

&#160;

==================

(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编码转换的过程:

①在Oracle客户端向办事器端提交SQL语句时,Oracle客户端按照NLS_LANG和数据库字符集,对从应用法度接传送过来的字符串编码进行转换处理惩罚。若是NLS_LANG与数据库字符集雷同,不作转换,不然要转换成数据库字符集并传送到办事器。办事器在接管到字符串编码之后,对于通俗的CHAR或VARCHAR2类型,直接存储;对于NCHAR或NVARCHAR2类型,办事器端将其转换为国度字符集再存储。

在查询数据时,办事器端原样返回存储在库中的数据,由客户端按照返回的元数据中的字符集信息与NLS_LANG和NLS_NCHAR的设置进行斗劲(若是NLS_NCHAR没有设置,则其默认值为NLS_LANG中的字符集设置),若是元数据中的字符集信息与客户端设置一致,不进行转换,不然要进行转换。国度字符集的转换按照NLS_NCHAR设置进行转换。

这里我也举几个应用SQLPLUS的测试例子,分别在A、B两库履行雷同的语句,然后经由过程收集抓包查看从Oracle client传输到办事器的具体内容。

例1 客户端NLS_LANG:WE8MSWIN1252

SQL号令:

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号令:

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号令:

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的影响

独一受客户端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还是会报错。


原文地址:http://www.byywee.com/page/M0/S776/776391.html



原创粉丝点击