首页 > 数据库 > Oracle > [转载]Oracle数据库字符集问题解析3
继续问题解答
[@more@]ygzeng:
收获很大,在测试过程中,增加了一些新的case,在测试过程中,发现“替代字符”往往容易转换成“靠”,“?”,“?”,“驴”,对这些乱码的产生原因有些不解!
1,数据库字符集设为gb2312,客户端设为WE8ISO8859P1,然后插入数据,读取数据
SET NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1
sqlplus system/manager@gb2312
select * from nls_database_parameters where PARAMETER='NLS_CHARACTERSET';
PARAMETER VALUE
--------------------------------- ---------------------------------
NLS_CHARACTERSET ZHS16CGB231280
insert into charset_test(server_charset,os_charset,client_charset,cn_char) values('GB2312','GB2312','ISO8859','中文');
commit;
column dump format a40
select server_charset,os_charset,client_charset,cn_char,dump(cn_char) dump from charset_test where client_charset='ISO8859' and os_charset='GB2312';
SERVER_ OS_CHAR CLIENT_ CN_CHAR DUMP
------- ------- ------- ------- ------------------------------
GB2312 GB2312 ISO8859 靠 Typ=1 Len=4: 214,208,206,196
可以看到存储是完全正确的,但是在读取的时候,出现“靠”(在gb2312中编码为191,191),我猜测,在这种环境下“替代字符”的编码是BF,由于两个替代字符BFBF,导致出现了一个“靠”
疑问:在Oracle中,各种环境下的替代字符是不是都是"?"?按照测试结果好像不是。另外,为什么显示结果“靠”(191,191)
在ISO8859 字符环境下,“替代字符”也应该是"?",在gb2312中,倒是有可能是中文“?”其编码是:163,191,但是无论如何,都不应该是”靠“
在服务器端字符集为iso8859,客户端字符集为:gb2312的环境下进行读取,存储测试!
SET NLS_LANG=AMERICAN_AMERICA.ZHS16CGB231280
sqlplus system/manager@iso8859
select * from nls_database_parameters where PARAMETER='NLS_CHARACTERSET';
PARAMETER VALUE
--------------------------------- ---------------------------------
NLS_CHARACTERSET WE8ISO8859P1
insert into charset_test(server_charset,os_charset,client_charset,cn_char) values('ISO8859','GB2312','GB2312','中文');
commit;
column dump format a40
select server_charset,os_charset,client_charset,cn_char,dump(cn_char) dump from charset_test where client_charset='GB2312' and os_charset='GB2312';
SERVER_CHARSET OS_CHARSET CLIENT_CHARSET CN_CHAR
-------------- -------------- -------------- --------------
DUMP
------------------------------
ISO8859 GB2312 GB2312 ??
Typ=1 Len=2: 191,191
疑问:为什么存储了191?难道191真的是某种字符集的“替代字符”?191但是显示结果为什么是“?”?
SET NLS_LANG=AMERICAN_AMERICA.UTF8
sqlplus system/manager@iso8859
insert into charset_test(server_charset,os_charset,client_charset,cn_char) values('ISO8859','GB2312','UTF8','中文');
commit;
column dump format a40
select server_charset,os_charset,client_charset,cn_char,dump(cn_char) dump from charset_test where client_charset='UTF8' and os_charset='GB2312';
SERVER_CHARSET OS_CHARSET CLIENT_CHARSET
--------------------- --------------------- ---------------------
CN_CHAR DUMP
--------------------- ------------------------------
ISO8859 GB2312 UTF8
驴驴 Typ=1 Len=2: 191,191
疑问:为什么191在UTF8下表示成为“驴”?
jeffli73:
感谢ygzeng朋友补充的例子,下面我就分析一下
请ygzeng朋友给出实验时数据库的版本、client端OS的类型、版本,因为这些因素有可能导致实验结果的差别。
1.数据库字符集设为gb2312,客户端设为WE8ISO8859P1。
insert into charset_test(server_charset,os_charset,client_charset,cn_char) values('GB2312','GB2312','ISO8859','中文');
我的实验环境是数据库与client端在一台PC机上,OS为windows20003,Oracle为10.1.0.2,字符集请选用GBK。
执行上述插入与你的结果不一样,请将你的实验环境再详细描述一下。
关于插入后查询结果:
select server_charset,os_charset,client_charset,cn_char,dump(cn_char) dump from charset_test where
client_charset='ISO8859' and os_charset='GB2312';
SERVER_ OS_CHAR CLIENT_ CN_CHAR DUMP
------- ------- ------- ------- ------------------------------
GB2312 GB2312 ISO8859 靠 Typ=1 Len=4: 214,208,206,196
是可以理解的,服务器端为gb2312,(214,208)与(206,196)在ISO8859-1中没有对应字符,需要转换为替换字符,从实验结果看转换为191(0xBF),而在ISO8859-1中191(0xBF)对应的字符为¿,好象一个倒写的?,而191-128(即去掉191二进制最高位的1)为63,我们可以查一下ASCII码,正好是问号(?)的编码,看来拿此字符作为WE8ISO8859P1的替换字符也没什么可奇怪的。
Oracle将(214,208,206,196)转换为(191,191),而client操作系统如你所说也采用GB2312,而(191,191)正是“靠”的编码,所以显示“靠”。
ygzeng:
jeffli73,我的数据库版本是8.1.7.0.0,客户端安装了英文版本的windows2000+sp4(地区设置为:中华人民共和国,默认语言是:简体中文),客户端的数据库版本是:9.2.0.5!
是不是在不同的字符集下面,"替代字符"都不一样,并且"替代字符"由服务器端的字符集决定?而且这些"替代字符"在不同的环境下可以相互替代,也就是说iso8859下的" ¿ "(BF),如果客户端是gb2312,就会显示"?"(A3BF)
jeffli73:
继续解释ygzeng朋友第二个实验结果
2.在服务器端字符集为iso8859,客户端字符集为:gb2312的环境下进行读取,存储测试!
insert into charset_test(server_charset,os_charset,client_charset,cn_char) values('ISO8859','GB2312','GB2312','中文');
select server_charset,os_charset,client_charset,cn_char,dump(cn_char) dump from charset_test where client_charset='GB2312' and os_charset='GB2312';
SERVER_CHARSET OS_CHARSET CLIENT_CHARSET CN_CHAR
-------------- -------------- -------------- --------------
DUMP
------------------------------
ISO8859 GB2312 GB2312 ??
Typ=1 Len=2: 191,191
客户端字符集为:gb2312,汉字'中文'无法转换到iso8859-1字符集,存储到数据库的字符为(191),再次证明了(191)为iso8859-1字符集的替换字符。
当查询时,(191)在GB2312中没有对应字符,需要转换为替换字符,即中文问号“?”(A3 BF)。
解释ygzeng朋友第三个实验结果
3.
SET NLS_LANG=AMERICAN_AMERICA.UTF8
client的设置本应与操作系统一致,在某些特殊情况下,为避免字符集转换,设置成与数据库字符集一致,而此时却人为设置成第三种字符集,肯定会造成混乱。
insert into charset_test(server_charset,os_charset,client_charset,cn_char) values('ISO8859','GB2312','UTF8','中文');
select server_charset,os_charset,client_charset,cn_char,dump(cn_char) dump from charset_test where client_charset='UTF8'
and os_charset='GB2312';
SERVER_CHARSET OS_CHARSET CLIENT_CHARSET
--------------------- --------------------- ---------------------
CN_CHAR DUMP
--------------------- ------------------------------
ISO8859 GB2312 UTF8
驴驴 Typ=1 Len=2: 191,191
此时'中文'实际用的GB2312的编码,而使用者却告诉Oracle这是UTF8的编码,转化为ISO8859-1,没有对应的字符,转换为替换字符(191,191)。
而在查询时,比较有意思,ISO8859-1的字符(191)可以转化为UTF8的字符,其对应字符为11000010 10111111(C2 BF),而操作系统使用的是GB2312编码,而(C2 BF)在GB码中对应的是“驴”字,所以显示为“驴”。
ygzeng:
jeffli73,关于实验3,你的解释是正确的!我后来查了一下,具体过程可能是这样的:
存储的时候,由于ISO8859并不支持中文,输入的“中文”只能用替代字符“¿”(191)来替代!
读取的时候,根据UCS-2和UTF8的转换规则2,如果字符位于:0080 - 07FF则取11位,转换成为:110xxxxx 10xxxxxx,191(BF)位于这个区间,根据这个规则,其转换成:(C2BF),由于操作体统是GB2312,在GB2312中,驴的编码是:C2BF
samechsu,数据库安装之后,字符集只能从子集向超集转换,两个不兼容的数据库的字符集不能转换的!
jeffli73:
关于实验3的解释
对于ygzeng朋友上述解释有一些不同意见
本来在解释实验3时,也想用UCS-2和UTF8的转换规则,但考虑到本例的源字符集为ISO8859,是一个8位的字符集,而UCS-2是一个16位的字符集,这么解释逻辑上并不是很通。所以如下解释可能更为合理一些:ISO8859中的字符“¿”(191)在UTF8中对应的字符编码为11000010 10111111(C2 BF)。
ygzeng:
Jeffli73,
Oracle中并不存在UCS(Unicode Character Set)这样的字符集,Unicode是以UTF8(UCS Transformation Format,变长字符集)或者UTF16(定长字符集)的形式存储的,你所说的“ISO8859中的字符“¿”(191)在UTF8中对应的字符编码为11000010 10111111(C2 BF)”,就是一个ucs-2到UTF8的转换过程,也就是,你的解释和我的解释好像没有什么区别!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29867/viewspace-809899/ ,如需转载,请注明出处,否则将追究法律责任。