字符集转换的问题~

时间:2022-01-11 08:40:23
刚在网上搜了些文章 可还是感觉自己对这个字符集理解的不够透彻 如果哪位有好点的文章给推荐一份吧

先来说说现在碰到的问题吧 如下面代码所示
C:\Documents and Settings\Admin>chcp
活动的代码页: 936
C:\Documents and Settings\Admin>set nls_lang=AMERICAN_AMERICA.UTF8

C:\Documents and Settings\Admin>sqlplus "/ as sysdba"

SQL*Plus: Release 10.2.0.1.0 - Production on Fri Oct 23 13:28:24 2009

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options
SQL> create table tt(name varchar2(100));

Table created.

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

1 row created.

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

1 row created.

SQL> commit;

Commit complete.

SQL> select * from tt;

NAME
--------------------------------------------------------------------------------




SQL>
SQL> Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.1.0
 - Production
With the Partitioning, OLAP and Data Mining options

C:\Documents and Settings\Admin>set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK

C:\Documents and Settings\Admin>sqlplus "/ as sysdba"

SQL*Plus: Release 10.2.0.1.0 - Production on 星期五 10月 23 13:29:49 2009

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


连接到:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> select * from tt;

NAME
--------------------------------------------------------------------------------


??

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

已创建 1 行。

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

已创建 1 行。

SQL> commit;

提交完成。

SQL> select * from tt;

NAME
--------------------------------------------------------------------------------


??



SQL>
SQL> 从 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options 断开

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

C:\Documents and Settings\Admin>sqlplus "/ as sysdba"

SQL*Plus: Release 10.2.0.1.0 - Production on Fri Oct 23 13:30:27 2009

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> select * from tt;

NAME
--------------------------------------------------------------------------------






SQL>


1、
用chcp显示出来的936应该代表的是我操作系统的字符集吧?

2、
为什么
用SIMPLIFIED CHINESE_CHINA.ZHS16GBK插入的数据在UTF8下显示不出来
用UTF8插入的数据在set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK后也显示不出来
一个显示乱码  一个显示问号?

3、
我的环境是WINDOWS XP+ORACLE 10GR2
记得前几天看EYGLE的一本书上写着字符集分三个
数据库字符集 客户端字符集 客户端应用字符集 
那SQL*PLUS算是客户端还是客户端应用呢

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
NLS_CALENDAR                   GREGORIAN
NLS_DATE_FORMAT                DD-MON-RR
NLS_DATE_LANGUAGE              AMERICAN
NLS_SORT                       BINARY
NLS_TIME_FORMAT                HH.MI.SSXFF AM

PARAMETER                      VALUE
------------------------------ ------------------------------
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY              $
NLS_COMP                       BINARY
NLS_LENGTH_SEMANTICS           BYTE
NLS_NCHAR_CONV_EXCP            FALSE
NLS_NCHAR_CHARACTERSET         UTF8
NLS_RDBMS_VERSION              10.2.0.1.0

20 rows selected.

SQL>

注册表里所有的NLS_LANG都设置的是AMERICAN_AMERICA.UTF8

希望通过这个帖子能让我彻底搞清楚字符集~大家帮帮忙~

19 个解决方案

#1


1.是。
2.两个编码方式长度都不一样。不是乱码倒怪了。
3.总之就是用你os的nls_lang环境变量中的字符集设置值。

#2


关注,学习

#3


引用 1 楼 vc555 的回复:
1.是。
2.两个编码方式长度都不一样。不是乱码倒怪了。
3.总之就是用你os的nls_lang环境变量中的字符集设置值。
第二点和第三点能讲得详细点吗

不是说如果ORACLE发现服务器端和客户端的字符集不一致的时候会自动进行转换的么

#4


引用 3 楼 wh62592855 的回复:
不是说如果ORACLE发现服务器端和客户端的字符集不一致的时候会自动进行转换的么

是的。但是oracle是怎么发现“服务器端和客户端的字符集不一致”的呢?
就是通过你设置的nls_lang参数中的字符集参数和数据库字符集对比知道的。
你把两者设置成一样,而os实际又没用nls_lang所指的字符集。这时insert数据的字符集就会出问题了。
具体你可以select name,dump(name) from t;看看。

#5


先问个问题哦
注册表里的那些NLS_LANG和我在命令行下set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK有什么关联吗?

如你所说,ORACLE是通过NLS_LANG来判断客户端和数据库字符集是否一致的。那么ORACLE是通过我在命令行里SET的NLS_LANG还是根据注册表中的NLS_LANG呢?二者间存在什么优先级关系吗?

呵呵 不要嫌麻烦哦 

#6


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

#7


5L的问题你自己搜搜吧。

给你的那个查询sql你要最好在两种nls_lang下插入相同的数据后,再执行。这样你就会发现在数据库的相同字符集下,相同的插入字符却有完全不同的编码。这就是乱码的原因。

#8


C:\Documents and Settings\Admin>set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK

C:\Documents and Settings\Admin>sqlplus "/ as sysdba"

SQL*Plus: Release 10.2.0.1.0 - Production on 星期五 10月 23 16:14:37 2009

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


连接到:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> select * from tt;

NAME
--------------------------------------------------------------------------------


??



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

已创建 1 行。

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

已创建 1 行。

SQL> commit;

提交完成。
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
一                             Typ=1 Len=3: 228,184,128
二                             Typ=1 Len=3: 228,186,140

已选择6行。


好的 谢谢回答~呵呵 我自己再想想

#9


这里很好解释呀

一个一个来
实验一
C:\Documents and Settings\Admin>set nls_lang=AMERICAN_AMERICA.UTF8
和你数据库的代码一致了。

所以这个过程Oracle将不会转码,你输入一个中文,你的系统是GBK的编码,又不发生转码,原封不动的过去了,所以你看到的 dump(name)时候,第一二两条记录都是2个字节。虽然你的Oracle客户端是UTF8.
-------------------- ------------------------------
一                    Typ=1 Len=2: 210,187
二                   Typ=1 Len=2: 182,254

实验二,同上理,
你这次是set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK和数据库不一致了,将把GBK转为UTF8,也就是2个字节变3个字节。 
也就是
三                             Typ=1 Len=3: 228,184,137
四                             Typ=1 Len=3: 229,155,155

实验三
第三次存的时候,和实验二一样
所以
一                             Typ=1 Len=3: 228,184,128
二                             Typ=1 Len=3: 228,186,140



#10


引用 9 楼 inthirties 的回复:
这里很好解释呀

 一个一个来
 实验一
 C:\Documents and Settings\Admin>set nls_lang=AMERICAN_AMERICA.UTF8
 和你数据库的代码一致了。

 所以这个过程Oracle将不会转码,你输入一个中文,你的系统是GBK的编码,又不发生转码,原封不动的过去了,所以你看到的 dump(name)时候,第一二两条记录都是2个字节。虽然你的Oracle客户端是UTF8.
 -------------------- ------------------------------
 一                    Typ=1 Len=2: 210,187
 二                   Typ=1 Len=2: 182,254

 实验二,同上理,
 你这次是set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK和数据库不一致了,将把GBK转为UTF8,也就是2个字节变3个字节。
 也就是
 三                             Typ=1 Len=3: 228,184,137
 四                             Typ=1 Len=3: 229,155,155

 实验三
 第三次存的时候,和实验二一样
 所以
 一                             Typ=1 Len=3: 228,184,128
 二                             Typ=1 Len=3: 228,186,140


上面是你实验 的结果, 完全是合理的,下面我们来解释一下,为什么有时候select是乱码,有时有时正常的,完全把你都搞糊涂了。

#11


本帖最后由 inthirties 于 2009-10-23 22:47:31 编辑
实验一,你应该不会有疑问,因为你知道客户端和服务器段一致。

关键是实验二
怎么我用SIMPLIFIED CHINESE_CHINA.ZHS16GBK的时候,看到了乱码,但是存进去却不是乱码。究竟是怎么回事呀。
我们来看 一,二两条记录,根据上面的回答,我们知道一,二进去的时候,没有转码,所以两个字节。 我们现在来显示,由于客户端是SIMPLIFIED CHINESE_CHINA.ZHS16GBK,和服务器不一致,这时候,服务器和客户端知道要转码了。由UTF8致GBK, 很显然,这里转是有错误的,所以一,二是乱码了。

在来看三 四, 三,四是在GBK的情况下进去的,这时候很显然了,不一致要转码,中文转为utf8,三个字节,显示的时候,一样的,不一致,要转码,本来进去就是utf8进去的,转成GBK当然没有问题了,所以也就正常显示了。

在看实验三

为什么我们set nls_lang=AMERICAN_AMERICA.UTF8后,再看刚刚做进去的三 四又乱码叻。

还是字符集的秘密,这是和服务器一致,不会转码,

一,二是GBK的两字节,既然不转码,过来就正常显示了。

三,四呢是UTF的三字节,现在不转码,三字节过来用GBK怎么能正常显示了。

当然就出现乱码了呀。

在字符集的问题中,抓住关键的地方,就是转码的问题,始终要记得,决定转码的是Oracle客户端和服务段的编码,和客户端系统的是没有关系的,虽然没有关系,只是决定显示的而已。

这里你可以在linux下用UTF8的系统, 做一下测试,你会发现和这里的结果又不一致。但是道理是相同的。

有兴趣,可以自己先分析一下结果,然后再做实验,看你是是不对的,如果分析对了,呵呵呵,我这里讲的就没有白费叻。

#12


呵呵 先回一个 
慢慢再看
免得你超过3次连续回帖限制

#13


自己对字符集方面的知识还是比较匮乏的

老大有没有什么好的文章推荐一份呀?
就比如说各种字符集的编码规则(比如说用2个字节还是3个字节...),他们之间的范围关系(比如说谁是谁的超级,谁是谁的子集),还有他们的转换关系(比如说从A到B可以转,而从B到A转就会出现乱码)

我刚才搜了下没搜到太好的  我再去搜搜看 不过马上就熄灯了  呵呵

#14


啊,我这里解释的还不清楚么。

#15


我觉得自己讲的很清楚了呀,

星期一看我的blog吧。

#16


    不错,收藏了

#17


引用 14 楼 inthirties 的回复:
啊,我这里解释的还不清楚么。
呵呵 你是讲的很清楚了呀
可是这里只涉及到了UTF8和GBK嘛 
我的意思就是说还有些其他字符集之间的关系之类的

#18


呵呵呵,清楚了就好,关注我周一早上的博客文章吧。

#19


恩 好的我会的  

好了
结贴

#1


1.是。
2.两个编码方式长度都不一样。不是乱码倒怪了。
3.总之就是用你os的nls_lang环境变量中的字符集设置值。

#2


关注,学习

#3


引用 1 楼 vc555 的回复:
1.是。
2.两个编码方式长度都不一样。不是乱码倒怪了。
3.总之就是用你os的nls_lang环境变量中的字符集设置值。
第二点和第三点能讲得详细点吗

不是说如果ORACLE发现服务器端和客户端的字符集不一致的时候会自动进行转换的么

#4


引用 3 楼 wh62592855 的回复:
不是说如果ORACLE发现服务器端和客户端的字符集不一致的时候会自动进行转换的么

是的。但是oracle是怎么发现“服务器端和客户端的字符集不一致”的呢?
就是通过你设置的nls_lang参数中的字符集参数和数据库字符集对比知道的。
你把两者设置成一样,而os实际又没用nls_lang所指的字符集。这时insert数据的字符集就会出问题了。
具体你可以select name,dump(name) from t;看看。

#5


先问个问题哦
注册表里的那些NLS_LANG和我在命令行下set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK有什么关联吗?

如你所说,ORACLE是通过NLS_LANG来判断客户端和数据库字符集是否一致的。那么ORACLE是通过我在命令行里SET的NLS_LANG还是根据注册表中的NLS_LANG呢?二者间存在什么优先级关系吗?

呵呵 不要嫌麻烦哦 

#6


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

#7


5L的问题你自己搜搜吧。

给你的那个查询sql你要最好在两种nls_lang下插入相同的数据后,再执行。这样你就会发现在数据库的相同字符集下,相同的插入字符却有完全不同的编码。这就是乱码的原因。

#8


C:\Documents and Settings\Admin>set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK

C:\Documents and Settings\Admin>sqlplus "/ as sysdba"

SQL*Plus: Release 10.2.0.1.0 - Production on 星期五 10月 23 16:14:37 2009

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


连接到:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> select * from tt;

NAME
--------------------------------------------------------------------------------


??



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

已创建 1 行。

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

已创建 1 行。

SQL> commit;

提交完成。
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
一                             Typ=1 Len=3: 228,184,128
二                             Typ=1 Len=3: 228,186,140

已选择6行。


好的 谢谢回答~呵呵 我自己再想想

#9


这里很好解释呀

一个一个来
实验一
C:\Documents and Settings\Admin>set nls_lang=AMERICAN_AMERICA.UTF8
和你数据库的代码一致了。

所以这个过程Oracle将不会转码,你输入一个中文,你的系统是GBK的编码,又不发生转码,原封不动的过去了,所以你看到的 dump(name)时候,第一二两条记录都是2个字节。虽然你的Oracle客户端是UTF8.
-------------------- ------------------------------
一                    Typ=1 Len=2: 210,187
二                   Typ=1 Len=2: 182,254

实验二,同上理,
你这次是set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK和数据库不一致了,将把GBK转为UTF8,也就是2个字节变3个字节。 
也就是
三                             Typ=1 Len=3: 228,184,137
四                             Typ=1 Len=3: 229,155,155

实验三
第三次存的时候,和实验二一样
所以
一                             Typ=1 Len=3: 228,184,128
二                             Typ=1 Len=3: 228,186,140



#10


引用 9 楼 inthirties 的回复:
这里很好解释呀

 一个一个来
 实验一
 C:\Documents and Settings\Admin>set nls_lang=AMERICAN_AMERICA.UTF8
 和你数据库的代码一致了。

 所以这个过程Oracle将不会转码,你输入一个中文,你的系统是GBK的编码,又不发生转码,原封不动的过去了,所以你看到的 dump(name)时候,第一二两条记录都是2个字节。虽然你的Oracle客户端是UTF8.
 -------------------- ------------------------------
 一                    Typ=1 Len=2: 210,187
 二                   Typ=1 Len=2: 182,254

 实验二,同上理,
 你这次是set nls_lang=SIMPLIFIED CHINESE_CHINA.ZHS16GBK和数据库不一致了,将把GBK转为UTF8,也就是2个字节变3个字节。
 也就是
 三                             Typ=1 Len=3: 228,184,137
 四                             Typ=1 Len=3: 229,155,155

 实验三
 第三次存的时候,和实验二一样
 所以
 一                             Typ=1 Len=3: 228,184,128
 二                             Typ=1 Len=3: 228,186,140


上面是你实验 的结果, 完全是合理的,下面我们来解释一下,为什么有时候select是乱码,有时有时正常的,完全把你都搞糊涂了。

#11


本帖最后由 inthirties 于 2009-10-23 22:47:31 编辑
实验一,你应该不会有疑问,因为你知道客户端和服务器段一致。

关键是实验二
怎么我用SIMPLIFIED CHINESE_CHINA.ZHS16GBK的时候,看到了乱码,但是存进去却不是乱码。究竟是怎么回事呀。
我们来看 一,二两条记录,根据上面的回答,我们知道一,二进去的时候,没有转码,所以两个字节。 我们现在来显示,由于客户端是SIMPLIFIED CHINESE_CHINA.ZHS16GBK,和服务器不一致,这时候,服务器和客户端知道要转码了。由UTF8致GBK, 很显然,这里转是有错误的,所以一,二是乱码了。

在来看三 四, 三,四是在GBK的情况下进去的,这时候很显然了,不一致要转码,中文转为utf8,三个字节,显示的时候,一样的,不一致,要转码,本来进去就是utf8进去的,转成GBK当然没有问题了,所以也就正常显示了。

在看实验三

为什么我们set nls_lang=AMERICAN_AMERICA.UTF8后,再看刚刚做进去的三 四又乱码叻。

还是字符集的秘密,这是和服务器一致,不会转码,

一,二是GBK的两字节,既然不转码,过来就正常显示了。

三,四呢是UTF的三字节,现在不转码,三字节过来用GBK怎么能正常显示了。

当然就出现乱码了呀。

在字符集的问题中,抓住关键的地方,就是转码的问题,始终要记得,决定转码的是Oracle客户端和服务段的编码,和客户端系统的是没有关系的,虽然没有关系,只是决定显示的而已。

这里你可以在linux下用UTF8的系统, 做一下测试,你会发现和这里的结果又不一致。但是道理是相同的。

有兴趣,可以自己先分析一下结果,然后再做实验,看你是是不对的,如果分析对了,呵呵呵,我这里讲的就没有白费叻。

#12


呵呵 先回一个 
慢慢再看
免得你超过3次连续回帖限制

#13


自己对字符集方面的知识还是比较匮乏的

老大有没有什么好的文章推荐一份呀?
就比如说各种字符集的编码规则(比如说用2个字节还是3个字节...),他们之间的范围关系(比如说谁是谁的超级,谁是谁的子集),还有他们的转换关系(比如说从A到B可以转,而从B到A转就会出现乱码)

我刚才搜了下没搜到太好的  我再去搜搜看 不过马上就熄灯了  呵呵

#14


啊,我这里解释的还不清楚么。

#15


我觉得自己讲的很清楚了呀,

星期一看我的blog吧。

#16


    不错,收藏了

#17


引用 14 楼 inthirties 的回复:
啊,我这里解释的还不清楚么。
呵呵 你是讲的很清楚了呀
可是这里只涉及到了UTF8和GBK嘛 
我的意思就是说还有些其他字符集之间的关系之类的

#18


呵呵呵,清楚了就好,关注我周一早上的博客文章吧。

#19


恩 好的我会的  

好了
结贴

#20