字符集问题的初步探讨(六)-----乱码的产生[1]

[入库:2005年8月18日] [更新:2007年3月24日]

本文简介:选择自 eygle 的 blog


原文链接:

http://www.eygle.com/special/nls_character_set_06.htm


 

原文发表于itpub技术丛书《oracle数据库dba专题技术精粹》,未经许可,严禁转载本文.

最后我们来讨论一下乱码的产生。

 

通常在我们的现实环境中,存在3个字符集设置。

第一: 客户端应用字符集(client application character set)

第二: 客户端nls_lang参数设置

第三: 服务器端,数据库字符集(character set)设置

 

我们说,一个字符在客户端应用(比如sqlplus,cmd,notepad等)中以怎样的字符显示取决于客户端操作系统,客户端能够显示怎样的字符,
我们就可以在应用中录入这些字符,至于这些字符能否在数据库中正常存储,就和另外的两个字符集设置紧密相关了。

在传输过程中,客户端nls_lang主要用于进行转换判断

如果nls_lang等于数据库字符集,则不进行任何转换直接把字符插入数据库

如果不同则进行转换,转换主要有两个任务

  • 如果存在对应关系,则把相应二进制编码经过映射后(这一步映射以后,所代表的字符可能发生转换)传递给数据库
  • 如果不存在对应关系,则传递一个替换字符(很多平台就是?)

 

数据库字符集,在和客户端nls_lang不同时,会把经过nls_lang转换的字符进行进一步处理

  • 对于?(即不存在对应关系的字符)直接以?形式存放入数据库
  • 对于其他字符,在nls_lang和数据库字符集之间进行转换后存入。

 

以下我们来看一下最为常见的字符集及乱码的产生:

1.当nls_lang字符集与数据库字符集不同,同时nls_lang不同于server端字符集设置

在这种情况下,存在两种可能:

  • 客户端输入的字符在nls_lang中没有对应的字符,这时无法转换,nls_lang使用替换字符替代这些无法映射的字符(这一步转换在tts中
    完成),在很多字符集中这个替代字符就是”?”
  • 当客户端的字符在nls_lang中对应了不同的字符时,传递给数据库以后发生转换,存储的是字符,但是已经丢失了元数据,数据库中
    的字符不再代表客户端的输入。而且这个过程不可逆,这也就是为什么很多时候在客户端输入的是正常的编码,查询之后会得到未知字符的原因。

我们通过上图来简单说明一下这个过程,当客户端在we8iso8859p15字符集时,输入欧元符号: €,这时客户端nls_lang和数据库端字符集不同,
进行第一次转换,客户端€符号编码是a4,在nls_lang转换时,a4对应了nls_lang中的‘¤’,这一步的转换产生了错误映射。由于数据库字符集不
同于nls_lang设置,这时进一步的转换发生了,存入数据库的编码变成了c2a4,虽然同nls_lang进行了正确的转换,但是客户端录入的数据已经
损坏或者丢失了。

我们可以用我们熟悉的字符集做一个简单的测试:

测试环境:

客户端应用为中文18030字符集

nls_lang设置为us7ascii字符集

数据库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('测试');

1 row created.

sql> select name,dump(name) from test;

name	dump(name)
--------------------------------------------------
2bjt	typ=1 len=4: 50,98,74,84

这时候我们发现,查询出来的是混乱的字符,我们把这些字符转换为2进制就是
110010   1100010   1001010   1010100
补全8位就是       00110010  01100010  01001010  01010100
我们把首位换成1   10110010  11100010  11001010  11010100

我们来看正确的存储:
c:\>set nls_lang=american_america.zhs16gbk

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:
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('测试');

1 row created.

sql> col dump(name) for a30
sql> select name,dump(name) from test;

name dump(name)
---------- ------------------------------
测试 typ=1 len=4: 178,226,202,212

1 row selected.

我们把这个结果转换为2进制表示
10110010 11100010 11001010 11010100

这个结果正是我们前面乱码首位补全1后的结果。

这个测试说明在us7ascii转换中文的时候除去了首位的 1,这样就丢失了元数据,导致乱码出现,nls_lang的转换作用由此可加一斑!

 

                                            

本文关键:字符集问题的初步探讨(六)-----乱码的产生
  相关方案
Google
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top