12
返回列表 发新帖
楼主: ClearWindxsw

[原创] 关于oracle 10g在utf8字符集下的rpad函数的疑惑

[复制链接]
论坛徽章:
47
蒙奇·D·路飞
日期:2017-03-27 08:04:23马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11一汽
日期:2013-09-01 20:46:27复活蛋
日期:2013-03-13 07:55:232013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:03:322012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:20
11#
发表于 2008-11-30 02:16 | 只看该作者
Why not use only one character in the string so the test is easier for everybody to read? And use dump() function to check the actual stored data.

Yong Huang

使用道具 举报

回复
论坛徽章:
8
2009新春纪念徽章
日期:2009-01-04 14:52:28祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:07:24ITPUB9周年纪念徽章
日期:2010-10-08 09:32:25ITPUB十周年纪念徽章
日期:2011-11-01 16:23:262013年新春福章
日期:2013-02-25 14:51:24沸羊羊
日期:2015-03-04 14:51:522015年新春福章
日期:2015-03-06 11:57:31
12#
发表于 2008-11-30 14:55 | 只看该作者
YONGHUAN说的是,我现在把字符‘⑥'在ZHS16GBK和UTF8下的测试情况贴一下,我觉得除了该字符在两种字符集下rpad应用中存在莫名差异外,其他没什么异常情况发现,该字符在两种字符集下,都是按照多字节存储的,而在UTF8下RPAD函数中却只被当做一个占位符,确实有点费解,大家可以看一下,红色表示的地方是指大家要注意的地方,开始是ZHS16GBK字符集下的,然后是UTF8下的,大家一起讨论:

Zhs16gbk:
SQL> SELECT RPAD('⑥',3,'-') FROM DUAL;
RPA
---
⑥-

SQL> SELECT '⑥' FROM DUAL;
'
--


SQL> SELECT LENGTH('⑥') FROM DUAL;
LENGTH('⑥')
------------
           1

SQL> SELECT LENGTHB('⑥') FROM DUAL;
LENGTHB('⑥')
-------------
            2

SQL> SELECT CONVERT('⑥','UTF8','ZHS16GBK') FROM DUAL;
SP2-0784: 返回的以 0xA5 开头的字符无效或不完整

SQL> SELECT LENGTH(CONVERT('⑥','UTF8','ZHS16GBK')) FROM DUAL;
LENGTH(CONVERT('⑥','UTF8','ZHS16GBK'))
---------------------------------------
                                      1

SQL> SELECT LENGTHB(CONVERT('⑥','UTF8','ZHS16GBK')) FROM DUAL;
LENGTHB(CONVERT('⑥','UTF8','ZHS16GBK'))
----------------------------------------
                                       3

SQL> select dump('⑥',1016) from dual;
DUMP('⑥',1016)
-----------------------------------------
Typ=96 Len=2 CharacterSet=ZHS16GBK: a2,de

SQL> select dump(CONVERT('⑥', 'UTF8', 'zhs16gbk'),1016) from dual;
DUMP(CONVERT('⑥','UTF8','ZHS16GBK'),1016)
-------------------------------------------
Typ=1 Len=3 CharacterSet=ZHS16GBK: e2,91,a5

Utf8:
SQL> SELECT RPAD('⑥',3,'-') FROM DUAL;
RPAD('⑥',
----------
⑥--

SQL> select '⑥' from dual;
'⑥'
------


SQL> SELECT LENGTH('⑥') FROM DUAL;
LENGTH('⑥')
------------
           1

SQL> SELECT LENGTHB('⑥') FROM DUAL;
LENGTHB('⑥')
-------------
            3

SQL>select convert('⑥','zhs16gbk','UTF8') from dual;
ERROR:
ORA-29275: 部分多字节字符

未选定行


SQL> select LENGTH(convert('⑥','zhs16gbk','UTF8')) from dual;
LENGTH(CONVERT('⑥','ZHS16GBK','UTF8'))
---------------------------------------
                                      1

SQL> select LENGTHB(convert('⑥','zhs16gbk','UTF8')) from dual;
LENGTHB(CONVERT('⑥','ZHS16GBK','UTF8'))
----------------------------------------
                                       2

SQL> select dump('⑥',1016) from dual;
DUMP('⑥',1016)
--------------------------------------------------------------------------------
Typ=96 Len=3 CharacterSet=UTF8: e2,91,a5

SQL> select dump(CONVERT('⑥', 'zhs16gbk', 'UTF8'), 1016) from dual;
DUMP(CONVERT('⑥','ZHS16GBK','UTF8'),1016)
------------------------------------------------------------------------
Typ=1 Len=2 CharacterSet=UTF8: a2,de

注意:type=1,代表VARCHAR2或NVARCHAR2;
      Type=96,代表CHAR或NCHAR;
见:Oracle® Database SQL Language Reference11g Release 1 (11.1) B28286-02   
P40 table2-1

使用道具 举报

回复
论坛徽章:
1
2009新春纪念徽章
日期:2009-01-04 14:52:28
13#
 楼主| 发表于 2008-12-3 10:49 | 只看该作者

简化测试


  1. Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.4.0
  2. Connected as ******

  3. SQL>
  4. SQL> --字符集
  5. SQL> SELECT USERENV('language') LANG  FROM DUAL;

  6. LANG
  7. ----------------------------------------------------
  8. SIMPLIFIED CHINESE_CHINA.UTF8
  9. SQL> --version
  10. SQL> SELECT VERSION FROM V$INSTANCE;

  11. VERSION
  12. -----------------
  13. 10.2.0.4.0
  14. SQL> ------默认情况下的特殊字符和'集'的显示及DUMP-----------
  15. SQL> --默认
  16. SQL> SELECT 'default' REMARK, '⑥' STR, LENGTH('⑥') L_STR,
  17.   2         LENGTHB('⑥') LB_STR, DUMP('⑥', 1016) D_STR
  18.   3    FROM DUAL
  19.   4  UNION
  20.   5  SELECT 'default' REMARK, '集' STR, LENGTH('集') L_STR,
  21.   6         LENGTHB('集') LB_STR, DUMP('集', 1016) D_STR
  22.   7    FROM DUAL
  23.   8  UNION --联合一下方便查看
  24.   9  --convert之后的
  25. 10  SELECT 'convert' REMARK,
  26. 11         CONVERT('⑥', 'zhs16gbk', 'UTF8') STR,
  27. 12         LENGTH(CONVERT('⑥', 'zhs16gbk', 'UTF8')) L_STR,
  28. 13         LENGTHB(CONVERT('⑥', 'zhs16gbk', 'UTF8')) LB_STR,
  29. 14         DUMP(CONVERT('⑥', 'zhs16gbk', 'UTF8'), 1016) D_STR
  30. 15    FROM DUAL
  31. 16  UNION
  32. 17  SELECT 'convert' REMARK,
  33. 18         CONVERT('集', 'zhs16gbk', 'UTF8') STR,
  34. 19         LENGTH(CONVERT('集', 'zhs16gbk', 'UTF8')) L_STR,
  35. 20         LENGTHB(CONVERT('集', 'zhs16gbk', 'UTF8')) LB_STR,
  36. 21         DUMP(CONVERT('集', 'zhs16gbk', 'UTF8'), 1016) D_STR
  37. 22    FROM DUAL;

  38. REMARK  STR      L_STR     LB_STR D_STR
  39. ------- --- ---------- ---------- ----------------------------------------
  40. convert ⑥           1          2 Typ=1 Len=2 CharacterSet=UTF8: a2,de
  41. convert 集           2          2 Typ=1 Len=2 CharacterSet=UTF8: bc,af
  42. default ⑥           1          3 Typ=96 Len=3 CharacterSet=UTF8: e2,91,a5
  43. default 集           1          3 Typ=96 Len=3 CharacterSet=UTF8: e9,9b,86
  44. SQL> -------------对RPAD进行的测试-------------
  45. SQL> SELECT 'default' REMARK, --默认情况下的RPAD
  46.   2         RPAD('⑥', 3, '-') STR,
  47.   3         LENGTH(RPAD('⑥', 3, '-')) L_STR,
  48.   4         LENGTHB(RPAD('⑥', 3, '-')) LB_STR,
  49.   5         RPAD('集', 3, '-') ZH,
  50.   6         LENGTH(RPAD('集', 3, '-')) L_ZH,
  51.   7         LENGTHB(RPAD('集', 3, '-')) LB_ZH
  52.   8    FROM DUAL
  53.   9  UNION
  54. 10  SELECT 'convert' REMARK, --convert后的RPAD
  55. 11         RPAD(CONVERT('⑥', 'zhs16gbk', 'UTF8'), 3, '-') STR,
  56. 12         LENGTH(RPAD(CONVERT('⑥', 'zhs16gbk', 'UTF8'), 3, '-')) L_STR,
  57. 13         LENGTHB(RPAD(CONVERT('⑥', 'zhs16gbk', 'UTF8'), 3, '-')) LB_STR,
  58. 14         RPAD(CONVERT('集', 'zhs16gbk', 'UTF8'), 3, '-') ZH,
  59. 15         LENGTH(RPAD(CONVERT('集', 'zhs16gbk', 'UTF8'), 3, '-')) L_ZH,
  60. 16         LENGTHB(RPAD(CONVERT('集', 'zhs16gbk', 'UTF8'), 3, '-')) LB_ZH
  61. 17    FROM DUAL;

  62. REMARK  STR        L_STR     LB_STR ZH         L_ZH      LB_ZH
  63. ------- ----- ---------- ---------- ---- ---------- ----------
  64. convert ?-             3          3 集-           3          3
  65. default ⑥--           3          5 集-           2          4

  66. SQL>
复制代码

这是自己在UTF8字符集下做的简化测试
结果:
1、把⑥转换一下,总长度是满足了,但又会引起识别不出来,显示一个?号,也不符合要求
2、集 这个字,经过转换后再LENGTH,得到的值是2,而⑥则还是1

[ 本帖最后由 ClearWindxsw 于 2008-12-3 14:30 编辑 ]

使用道具 举报

回复
论坛徽章:
1
2009新春纪念徽章
日期:2009-01-04 14:52:28
14#
 楼主| 发表于 2008-12-3 10:59 | 只看该作者
sqysl兄,你的是oracle 11g吗,怎么convert转换会报错,我的执行倒是没报错

使用道具 举报

回复
论坛徽章:
1
2009新春纪念徽章
日期:2009-01-04 14:52:28
15#
 楼主| 发表于 2008-12-3 16:28 | 只看该作者

  1. --字符集
  2. SQL> SELECT USERENV('language') LANG  FROM DUAL;

  3. LANG
  4. ----------------------------------------------------
  5. SIMPLIFIED CHINESE_CHINA.ZHS16GBK

  6. SQL> --version
  7. SQL> SELECT VERSION FROM V$INSTANCE;

  8. VERSION
  9. -----------------
  10. 10.2.0.1.0

  11. SQL> ------默认情况下的特殊字符和'集'的显示及DUMP-----------
  12. SQL> --默认
  13. SQL> SELECT 'default' REMARK, '⑥' STR, LENGTH('⑥') L_STR,
  14.   2         LENGTHB('⑥') LB_STR, DUMP('⑥', 1016) D_STR
  15.   3    FROM DUAL
  16.   4  UNION
  17.   5  SELECT 'default' REMARK, '集' STR, LENGTH('集') L_STR,
  18.   6         LENGTHB('集') LB_STR, DUMP('集', 1016) D_STR
  19.   7    FROM DUAL
  20.   8  UNION --联合一下方便查看
  21.   9  --convert之后的
  22. 10  SELECT 'convert' REMARK,
  23. 11         CONVERT('⑥', 'UTF8', 'zhs16gbk') STR,
  24. 12         LENGTH(CONVERT('⑥', 'UTF8', 'zhs16gbk')) L_STR,
  25. 13         LENGTHB(CONVERT('⑥', 'UTF8', 'zhs16gbk')) LB_STR,
  26. 14         DUMP(CONVERT('⑥', 'UTF8', 'zhs16gbk'), 1016) D_STR
  27. 15    FROM DUAL
  28. 16  UNION
  29. 17  SELECT 'convert' REMARK,
  30. 18         CONVERT('集', 'UTF8', 'zhs16gbk') STR,
  31. 19         LENGTH(CONVERT('集', 'UTF8', 'zhs16gbk')) L_STR,
  32. 20         LENGTHB(CONVERT('集', 'UTF8', 'zhs16gbk')) LB_STR,
  33. 21         DUMP(CONVERT('集', 'UTF8', 'zhs16gbk'), 1016) D_STR
  34. 22    FROM DUAL;

  35. REMARK  STR      L_STR     LB_STR D_STR
  36. ------- --- ---------- ---------- -------------------------------------------
  37. convert 鈶?         1          3 Typ=1 Len=3 CharacterSet=ZHS16GBK: e2,91,a5
  38. convert 闆?         1          3 Typ=1 Len=3 CharacterSet=ZHS16GBK: e9,9b,86
  39. default ⑥           1          2 Typ=96 Len=2 CharacterSet=ZHS16GBK: a2,de
  40. default 集           1          2 Typ=96 Len=2 CharacterSet=ZHS16GBK: bc,af

  41. SQL> -------------对RPAD进行的测试-------------
  42. SQL> SELECT 'default' REMARK, --默认情况下的RPAD
  43.   2         RPAD('⑥', 3, '-') STR,
  44.   3         LENGTH(RPAD('⑥', 3, '-')) L_STR,
  45.   4         LENGTHB(RPAD('⑥', 3, '-')) LB_STR,
  46.   5         RPAD('集', 3, '-') ZH,
  47.   6         LENGTH(RPAD('集', 3, '-')) L_ZH,
  48.   7         LENGTHB(RPAD('集', 3, '-')) LB_ZH
  49.   8    FROM DUAL
  50.   9  UNION
  51. 10  SELECT 'convert' REMARK, --convert后的RPAD
  52. 11         RPAD(CONVERT('⑥', 'UTF8', 'zhs16gbk'), 3, '-') STR,
  53. 12         LENGTH(RPAD(CONVERT('⑥', 'UTF8', 'zhs16gbk'), 3, '-')) L_STR,
  54. 13         LENGTHB(RPAD(CONVERT('⑥', 'UTF8', 'zhs16gbk'), 3, '-')) LB_STR,
  55. 14         RPAD(CONVERT('集', 'UTF8', 'zhs16gbk'), 3, '-') ZH,
  56. 15         LENGTH(RPAD(CONVERT('集', 'UTF8', 'zhs16gbk'), 3, '-')) L_ZH,
  57. 16         LENGTHB(RPAD(CONVERT('集', 'UTF8', 'zhs16gbk'), 3, '-')) LB_ZH
  58. 17    FROM DUAL;

  59. REMARK  STR      L_STR     LB_STR ZH        L_ZH      LB_ZH
  60. ------- --- ---------- ---------- --- ---------- ----------
  61. convert 鈶-          2          3 闆-          2          3
  62. default ⑥-          2          3 集-          2          3

  63. SQL>
复制代码


以上结果是让同事在其他数据库上执行的结果
结果:长度是没问题,但转换全成了乱码

使用道具 举报

回复
论坛徽章:
8
2009新春纪念徽章
日期:2009-01-04 14:52:28祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:07:24ITPUB9周年纪念徽章
日期:2010-10-08 09:32:25ITPUB十周年纪念徽章
日期:2011-11-01 16:23:262013年新春福章
日期:2013-02-25 14:51:24沸羊羊
日期:2015-03-04 14:51:522015年新春福章
日期:2015-03-06 11:57:31
16#
发表于 2008-12-3 21:00 | 只看该作者
我的是11g,其实,我们通过测试都可以看到:
在转换过程中,字符编码及存储长度都正常,只是涉及到RPAD函数的问题,RPAD函数到底是根据什么运算的呢?肯定不是根据编码,因为,我们看到了转换前和转换后的编码都是相同的,不同的只是数据类型,难道是因为数据类型的变化吗?还有,为什么出现乱码吗?是因为我们环境的设置问题,但可以肯定的是显示问题,实际的编码是正确的。有待于研究。

使用道具 举报

回复
论坛徽章:
1
2009新春纪念徽章
日期:2009-01-04 14:52:28
17#
 楼主| 发表于 2008-12-4 09:22 | 只看该作者
sqysl兄,在SQL Reference 10gr2.pdf中对RPAD的解释原文如下:
The argument n is the total length of the return value as it is displayed on your
terminal screen. In most character sets, this is also the number of characters in the
return value. However, in some multibyte character sets, the display length of a
character string can differ from the number of characters in the string.
oracle自己也知道对多字节的字符集中,显示的最终结果会与实际的n不相符,还以为他们知道后会解决,但没想到在老兄所使用的11g中,非但没解决,还看到convert函数在转换后会报错,至少10g还没报这个错,唉。

要不是他们自己写着会不同,我就发到metalink上去了。
反正我现在对这种需要有定长输出到文本中的,我都使用自己对RPAD封装后的自定义函数去实现,LPAD也是如此。

使用道具 举报

回复
论坛徽章:
1
2009新春纪念徽章
日期:2009-01-04 14:52:28
18#
 楼主| 发表于 2008-12-4 09:24 | 只看该作者
编码和显示的问题,还要请对字符集编码有研究的同学帮忙分析一下了。最好能详细的解释一下,毕竟现在都在慢慢使用UNICODE了。

使用道具 举报

回复
论坛徽章:
8
2009新春纪念徽章
日期:2009-01-04 14:52:28祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:07:24ITPUB9周年纪念徽章
日期:2010-10-08 09:32:25ITPUB十周年纪念徽章
日期:2011-11-01 16:23:262013年新春福章
日期:2013-02-25 14:51:24沸羊羊
日期:2015-03-04 14:51:522015年新春福章
日期:2015-03-06 11:57:31
19#
发表于 2008-12-4 10:33 | 只看该作者
原帖由 ClearWindxsw 于 2008-12-4 09:22 发表
sqysl兄,在SQL Reference 10gr2.pdf中对RPAD的解释原文如下:
The argument n is the total length of the return value as it is displayed on your
terminal screen. In most character sets, this is also the number of characters in the
return value. However, in some multibyte character sets, the display length of a
character string can differ from the number of characters in the string.
oracle自己也知道对多字节的字符集中,显示的最终结果会与实际的n不相符,还以为他们知道后会解决,但没想到在老兄所使用的11g中,非但没解决,还看到convert函数在转换后会报错,至少10g还没报这个错,唉。

要不是他们自己写着会不同,我就发到metalink上去了。
反正我现在对这种需要有定长输出到文本中的,我都使用自己对RPAD封装后的自定义函数去实现,LPAD也是如此。


其实,我们可以看到,rpad中的n,是指的占位符数,length函数返回的是字符个数,它们不是一回事;
但在我们的测试中,RPAD和LENGTH函数在不进行字符集转换时是没问题的,在字符集转换后这些有关字符个数和占位符数的问题上就出现了问题,还有在tuf8中,"⑥"存储占用了三个字节,而在RPAD中,却用了一个占位符,老弟不妨发到metalink上问一下。

使用道具 举报

回复
论坛徽章:
1
奥运会纪念徽章:现代五项
日期:2008-10-24 13:26:49
20#
发表于 2009-8-25 09:55 | 只看该作者
楼主,我也碰到和你同样的问题,比你晚了8个月。

我碰的字和你不同,道理一样。特殊汉字“?”,为上面一个龙,下面一个天,飞龙在天的意思。
数据库环境也是UTF-8。
看你之前的测试,在zhs16gbk字符集下,是不存在该问题的。

我的理解:在UTF-8中汉字存储占三个字节,字母占一个字节。而zhs16gbk中汉字存储占两个字节。
RPAD函数在判断“?”或“⑥”实际判断为1,而实际存储时却占了3位。
导致RPAD执行后实际的要长N位,N=特殊字符的个数。

楼主的解决方法时改写rpad函数,我用了另一种方法,供参考一下。
使用函数to_nchar将字符先转换一下就不会有问题了。

SQL> select RPAD(nvl('⑥',nvl(' ',' ')), 10, nvl(' ',' ')),length(RPAD(nvl('⑥',nvl(' ',' ')), 10, nvl(' ',' '))) from dual;

RPAD(NVL('⑥',NVL('','')),10,N  LENGTH(RPAD(NVL('⑥',NVL('',''
------------------------------ ------------------------------
⑥                                                         10
SQL> select RPAD(nvl(to_nchar('⑥'),nvl(' ',' ')), 10, nvl(' ',' ')),length(RPAD(nvl(to_nchar('⑥'),nvl(' ',' ')), 10, nvl(' ',' '))) from dual;

RPAD(NVL(TO_NCHAR('⑥'),NVL(''  LENGTH(RPAD(NVL(TO_NCHAR('⑥')
------------------------------ ------------------------------
⑥                                                          9

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表