剪不断,理还乱--Oracle的字符集乱码问题

网友投稿 295 2022-09-14

剪不断,理还乱--Oracle的字符集乱码问题

作者:​​三十而立​​

咋又乱码了,Oracle的管理员经常会碰到字符乱码的问题,明明看到很的文档里都提到过只要客户端和服务器端的字符集一致。就能解决乱码问题。

可是我这里明明服务器和客户端字符集一致却显示的时候是乱码,客户端和服务器字符集不一致,反而到不显示乱码了。真是越整越糊涂,剪不断,理还乱。

这不,昨天CSDN上的,一个网友在有关字符集的实验中,就碰到这个问题叻,怎么想都想不通,陷入了上面的疑惑之中,只得在CSDN上求助。

我们先一起看看他的问题和相关的实验吧。

实验主要是针对字符集为研究目的来做的。

Oracle数据库服务器的字符集

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               UTF8

.....................

用一个表来做中文插入和查询的实验

SQL  >   create   table   tt(name   varchar2  (  100   ));

当前系统的字符集

C:Documents   and   SettingsAdmin  >  chcp

活动的代码页:   936

这里chcp查询的是window系统的cmd的字符集编码,当前的cmd的编码是GBK

下面就就设置不同的客户端的字符集来进行插入中文的测试叻。

先设置客户端的字符集编码和服务器的一致

C:Documents   and   SettingsAdmin  >  set   nls_lang  =  AMERICAN_AMERICA.UTF8

下面插入数据

(实验一)

SQL>  insert    into    tt    values   (   '   一   '   );

1 row created.

SQL>  insert    into    tt    values   (   '   二   '   );

1 row created.

SQL> commit;

SQL  >   select   *   from   tt;

NAME

--  --

这里一切都还正确,服务器和客户端的字符集一致,这里显示没有乱码。

接下来的实验,就开始让人琢磨不透了

(实验二)

现在切换客户端的字符集到GBK的字符集。

C:Documents   and   SettingsAdmin  >  set   nls_lang  =  SIMPLIFIED CHINESE_CHINA.ZHS16GBK

先看看上面的记录对不对

SQL  >   select   *   from   tt;

NAME

--  ------------------------------------------------------------------------------

??

乱码出现,这时候,我们可能对结果还比较肯定,还可以很理直气壮的,服务器和客户端的字符集不一致,当然就乱码了呀。那么看看下面的吧。

SQL  >   insert   into   tt   values  (  '  三  '  );

已创建   1    行。

SQL  >   insert   into   tt   values  (  '  四  '  );

已创建   1    行。

SQL  >   commit  ;

SQL  >   select   *   from   tt;

NAME

--  ------------------------------------------------------------------------------

??

奇怪了吧,上面还理直气壮的说过,由于服务器和客户端字符集不一致,应该是乱码的,怎么这后面两个,却不是乱码了。显示的居然还是正确的叻。

呵呵呵,是不是乱了呀。 先不忙着给你解释,让你先继续下面的实验,

客户端再次切换到UTF8的字符集上

(实验三)

C:/Documents and Settings/Admin>set nls_lang=AMERICAN_AMERICA.UTF8

再来看看上面的结果

SQL> select * from tt;

NAME

--------------------------------------------------------------------------------

这三和四又不知道变成什么样子了,反正是没有正确显示出来,究竟是怎么回事呀,这客户端和服务器端是一样的字符集,按道理,应该不乱码的,咋这里又乱码了,上面三,四在不一样的字符集倒没有乱码,倒是把人整迷糊叻。究竟是怎么回事呢?

下面我们来揭开这个谜团吧。

先用dump函数看看我们的存的字符的真面目吧。

SQL> select name,dump(name) from tt;

NAME DUMP(NAME)

------------------------------ ------------------------------

一 Typ=1 Len=2: 210,187

二 Typ=1 Len=2: 182,254

涓 Typ=1 Len=3: 228,184,137

鍥 Typ=1 Len=3: 229,155,155

dump揭露了我们存储的真面目,dump不做详细介绍了。有兴趣的可以去查查文档

这里可以看都是一个中文,一,二存进去只有两个字节,而三,四存进去是3个字节,究竟是怎么回事叻。导致这里的差异的就是字符集的原因。

还记得,一,二是用UTF8的客户端存进去的吧。对于Oracle来说,Oracle认识的客户端字符集,系统的字符集他是不关心的,当服务器字符集和客户端字符集一致的时候,Oracle在客户端和服务器端交换的时候,不会做字符集的转换,这样,我们输入的中文编码两个字节,原封不动的交给服务器端,服务器存到数据库里,也就是2个字节叻。后面的三,四的时候,客户端字符集是GBK,和服务器不一致,所以在服务端和客户端交互中,发生转码,这里客户端的中文编码转换到UTF8,是三个字节,这样三,四存到数据库里都是三个字节叻。

既然是这样,那么用UTF8看到的一,二是正常的,他们存的是GBK编码的字节,而三,四存的是UTF8,和你的客户端一致的字符集,反倒是不正常的叻。问题还是用上面的的原因来解释,根据dump的信息,我们可以看到,一二,是以GBK存储的,三,四是用UTF8编码存的。当我们select的时候,数据库找到了一二三四的数据,返回给Oracle客户端,当UTF8时,和数据库字符集一致,这个过程不发生转码,所以这里的字节原封不动的传到了客户端显示,一,二是GBK编码的2个字节,显示的时候,我们的客户端的系统的cmd是GBK的字符集,也就正常显示了。而三,四叻。传过来的是UTF8编码的字节,在GBK的cmd环境里,当然就不能正常显示了哟,也就出现了怪异的字符了。

当我们的客户端的字符集为GBK的时候呢。和服务器端的不一致,那么这个过程中服务器到客户端的过程中会进行转码,有UTF8转至到GBK, 而一,二已经是GBK的2个字节了,这个过程肯定不对,所以出现乱码,这也就是实验三的结果。而三,四呢,本身存的就是UTF8编码的三个字节,其实就是有GBK刚才转成的,现在回过去,当然是可以转变为GBK的编码了。然后cmd是GBK的编码,当然就正常的显示了。

也就出现了实验二的结果。

通过这里的实验,我们的分析的依据就是服务器段和Oracle客户端的字符集的一致的关系,如果不一致,在服务器和Oracle客户端交互的过程中,将会按两种字符集编码进行转码,反之,如果一致的话,在交互的过程中,不进行编码,字符直接的进行传输。 这就是这里最基本的分析原理了。

安装我们的分析原理,我们一起来猜想一下,我现在是如果客户端换在linux的utf8的操作系统环境中,

现在我们的Oracle的客户端环境的字符集是utf8

[oracle$ oracle]export nls_lang=AMERICAN_AMERICA.UTF8

在这样的sqlplus环境中,我们还是访问上的表

SQL> select name,dump(name) from tt;

这时候的结果,将如何显示叻?这里的一,二,三,四四条记录,是和我们的实验三(Oracle的客户端也是UTF8的编码)一样的结果嘛?

如果我们在这个Linux的sqlplus的环境中,再次插入五,六两条记录,他们又是怎样的,这个新插入的,五,六两条记录在实验二和实验三的环境里进行查询时,又会出现什么样的情况叻?

最后留下的这两个问题,就是对大家的检验,如果大家可以分析出结果,并能够尝试一下的话,那么大家就不再怕这个剪不断,理还乱的Oracle数据库的字符乱码的问题叻。

有兴趣,并且懂java的朋友,可以用java程序再来访问一下这里的所有数据,看又是什么样的情况?提示,java的thin的连接也是Oracle的客户端,但是不像oci以及其他客户端的程序那样,java不需要指定客户端的字符集。

做实验的过程中有问题的朋友,可以发邮件把你的问题反馈给我,我们一起讨论。

如果没有那句“三十而立”,三十岁的男人正可以轻轻松松

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:广告情报局:天猫:为女性写诗!
下一篇:私域流量之后应该怎么玩?
相关文章

 发表评论

暂时没有评论,来抢沙发吧~