pl/sql developer oracle生僻字显示问题

文章详细探讨了Oracle数据库在存储和显示生僻字时遇到的问题,包括数据库和客户端字符集设置、PL/SQLDeveloper的显示问题、JDBC与PL/SQL之间的差异。作者分析了字符编码的原理,如UTF-8和GBK的转换错误导致的“锟斤拷”现象,以及如何通过dump函数和网络抓包诊断问题。解决方案包括使用NVARCHAR2和AL16UTF16字符集以实现跨工具的兼容性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题由来
       随着国标GB18030的推行,各行各业都在如火如荼的落实改造。自己在工作中也发现很多问题,查阅了很多资料都未解决自己的问题。经过慢慢摸索,对生僻字经常出现的问题进行总结,现分享如下。

二、问题描述

1. oracle存储不了生僻字。

2. 在PL/SQL Developer 中输入生僻字时,该生僻字无法正常显示。

3. pl/sql能录入能显示,通过jdbc不能显示。

4. jdbc能录入能显示,通过pl/sql不能显示。

三、原因

1.对于数据库而言,不区分什么生僻字,它仅仅是存储一堆的字节而已。能否存储生僻字主要是看以下几个方面:

a.数据库自身设置的字符集(该字符集会影响jdbc或odbc驱动与服务器的传输编码,不像mysql那样可以指定传输编码。这个没有找到相关资料辅佐,有兴趣可以抓包验证),这里面有两个关键的参数,一个是针对varchar2,一个是针对nvarchar2,这两个的区别可自行百度,资料比较多。

如图所示,我自己的数据库varchar2类型我设置的是AL32UTF8(all language 32位即4个字节 utf-8编码)。nvarchar2我也是用的utf-8。utf8和gbk的区别这儿也不做描述,百度资料很多。这儿可以看到我的数据库是大字符集的,能存储生僻字。

b.客户端的字符集,这个可通过环境变量或注册表等修改(环境变量名为NLS_LANG,不会修改可自行百度)。

如图,我的客户端也是使用utf-8编码。网上的很多oracle显示中文乱码的问题差不多就到这儿了,确定了这两步差不多就可以解决中文乱码问题了。但是现在要说的是生僻字,为啥一会儿能显示一会儿又不能显示让人很迷惑,下面一一讲解。

2.数据库能存储生僻字后,想着插入一个生僻字来做测试,但是pl/sql却不能显示该生僻字,这个原因首先取决于你有没有安装能显示相关生僻字的字库(目前市面的大多字库都不能显示生僻字),其次是pl/sql工具要选择生僻字的字库,这样在pl/sql的编辑界面和结果界面才能显示生僻字。

3.设计了一张varchar2字段的表,pl/sql能插入和显示生僻字,但是程序(jdbc或odbc)读取不到该字。该字“𫍣”为生僻字,没相关字库是显示不出来的。

 如图上,该字通过pl/sql能插入,也能显示,以为没有没有问题了,但是发现用程序读取时,却是无法解析,若是应用程序的话那就乱码了,例如我通过jdbc读取如下:

当看到-17,-65,-67这几个字符时, 条件反射想到这是转码有问题。在unicode编码里面,有几个特殊的码点可以记一下,对查问题时很有用。

其中字符�,码点为FFFD,utf-8编码为 EF,BF,BD,即-17,-65,-67。该特殊码点表示替换字符,当你给定一段字节信息,用UTF-8解码时,如果解码失败,就会把该字转换为�。那为啥会存在解码失败,原因就是你的字节信息不是UTF-8的编码,但你却用UTF-8解码,那当然就会失败。最常见的就是你的字节流是GBK的,但解码时用UTF-8,这样就会存在字符替换。如下图:

 顺便一提,还有一个常见的字符显示错误还有一个:锟斤拷。这个又是怎么来的?同上面的道理,GBK转到UTF-8失败后,得到的是字符�,当有用该字符的字节信息通过GBK解码时,因为一个汉字只用两个字节,该字解出来就是我们看到的锟斤拷,如下图:

自此大家看到 �或者锟斤拷的时候,就知道是编码不一致问题,不用再好奇星号怎么来的。

回到主题,为啥jdbc读到的是-17,-65,-67,是存储了错误的字节?还是传输时转码了字节流信息?所幸oracle提供了一个函数可以查看数据库存储的究竟是什么字节——dump函数,函数具体怎么使用还是自行百度,下面看一下数据库里面存储的究竟是啥。

 存储了6个字节,而我们数据库设置的字符集为utf8,这个生僻字utf8编码却是F0,AB,8D,A3这4个字节,显然差异不是一点半点。那数据库中存储的这个字节到底怎么来的?我们知道不管jdbc还是pl/sql等其他工具,最终和服务器通信就是网络包,而包里的内容若没加密就是一个个字节流(即使加密也是一个个字节,只是读不出来)而已,既然这样,直接抓包看看就知道了。抓包结果如下图:

        还好oracle的网络通讯没有加密,很容易就找到发送的包信息,从上图可以很清晰的看出oracle存储的字符集是由客户端获取的,而至于pl/sql客户端获取该字的utf8码为什么是一个6字节,这个就不清楚了,有知道原因的同学可以告知一下。猜测这个是系统开发环境有关(可以这么理解,如果你的工具支持unicode,那编码解码就是我们理解utf的规则,例如自己写的工具JOV,用的VS2013开发的,平台支持unicode,生僻字字符是用是4个字节表示,其实就是utf-16。而pl/sql,xshell等工具应该是不支持unicode,遇到生僻字这种会进行转码表示,不知道具体是什么规则,但肯定有东西在里面的,不然也不会获取到6个字节,正常的汉字获取的utf8编码大家都是一样的),从码值上看正常的utf8是F0,AB,8D,A3,pl/sql获取到的是ed,a1,ac,ed,bd,a3,可以看到最后一个字节始终是相同的,他们之间的转换规则有知道的可以告知一下。

       到此可以理解为什么pl/sql能插入和显示生僻字,而其他jdbc获取到的却是乱码的问题了,根本原因是你用的工具能否支持unicode,虽然你的数据库字符集已经设置是utf8了,但真正存储的是你工具传给oracle的utf8的字节信息,你传入的是非主流的utf8(如上存入的ed,a1,ac,ed,bd,a3),别人肯定就读不出来(别人只认识F0,AB,8D,A3)。

       同理,回答第四个问题,原因也是一样的,用你的程序(以java为例,java肯定支持unicode的,你获取utf8很容易getBytes("utf-8")就可以获得,该值获得的就是正常的字节信息)插入该生僻字后,数据库中存储的就是F0,AB,8D,A3,而pl/sql读取生僻字它有他的规则,它认识的是6个字节的那个,4个字节的它就不认识了,如下图:

       可以看到自己的程序插入和显示都正常,但是pl/sql工具却把它当两个汉字来处理了,很好理解嘛,因为oracle存储了4个字节(一般情况下gbk一个普通汉字占2个字节,一个普通汉字utf8占3个字节),两个汉字它就当gbk码处理了, 下图也可以验证。

      至此oracle生僻字不能显示或不能存储的问题差不多描述清楚了,也解释了为什么,还有不明白的就多读几遍吧。文章中使用的工具:pl/sql developer, wireshark, eclipse, jov。jov工具已开源,有兴趣的可以去之前的文章找,最新版也可以去下载。https://download.youkuaiyun.com/download/jerome_jun/87815045

      还有一个问题,有人想我既想在pl/sql工具里面插入查看,又想应用程序能正确读取,那有办法吗?答案当然是肯定的,既然都知道了不能读取的原因,oracle如何存储信息,那当然可以解决。思路有两种:一是不改变现在的数据库字符集信息,自己写一个小工具提供插入和显示,需要支持更改字库,支持unicode编码,项目也不难,有兴趣的可以去写一个,没编程能力的当我白说。第二种方式是还记得上面提到的NVARCHAR2吗,这个设计本身就是针对这种字符集的情况,申请到的字符长度是多少就是多少,它也是可以设置字符集的,那究竟设置成啥呢?AL16UTF16了解一下,老规矩自行百度这个编码规则是啥。其实很多应用他们支持unicode码时,具体的表现形式用的就是UTF-16,像之前提到的VS2013和java,它的中文unicode码都是用utf-16存储的,优点缺点大家也可以自行去百度。回到主题,当你的NVARCHAR2设置为AL16UTF16后,你会发现ps/sql工具获取的utf-16编码和其他工具都一样了,大家存储的字节信息都一致了,这样大家就能正常访问了。那你可能会问varchar2直接设置成AL16UTF16好了嘛,不同工具不是都可以插入、修改、查询了嘛,很会举一反三,要是地主家儿子你是可以这么干的,这个问题嘛可以看看chatGPT怎么回答的:

     

整个排查问题分享得差不多了,有不恰当之处欢迎指导。

Oracle数据库中,要将汉字转换为带空格间隔的拼音,可以使用自定义函数`fn_getpyKGJG`。首先,确保你已经根据提供的指南创建了`fn_getpyKGJG`函数。此函数能够将汉字字符串转换为带空格分隔的拼音字符串,支持不同大小写的格式,并且能够处理大部分汉字,包括生僻字。 参考资源链接:[Oracle汉字转拼音函数创建与使用指南](https://wenku.youkuaiyun.com/doc/647952f0543f844488195f8d?spm=1055.2569.3001.10343) 要处理生僻字的转换,`fn_getpyKGJG`函数内部可能使用了特定的字典或者映射表来匹配汉字到其对应的拼音。这一步是关键,因为生僻字可能在标准的拼音转换库中找不到对应的拼音,所以需要额外的字典来确保转换的准确性。 在创建函数时,可以参考《Oracle汉字转拼音函数创建与使用指南》中的详细代码示例。这里是一个函数使用的基本步骤: 1. 在SQL*Plus或SQL Developer中,登录到Oracle数据库。 2. 执行创建函数`fn_getpyKGJG`的代码。这可能涉及到编写PL/SQL代码,创建存储过程。 3. 调用`fn_getpyKGJG`函数进行测试。例如,通过执行以下SQL语句: ```sql SELECT fn_getpyKGJG('汉字', 1) FROM dual; ``` 这条语句会返回带有空格间隔的全大写拼音。 4. 对于生僻字,确保数据库中包含了相应的字典文件和映射表,以供函数使用。这通常需要管理员权限来配置和更新Oracle的NLS参数。 5. 测试生僻字的转换效果,确保所有需要转换的汉字都能得到正确处理。 通过上述步骤,你可以在Oracle数据库中实现汉字到带空格间隔拼音的转换,并且正确处理生僻字问题。如果你希望进一步深入学习,建议详细阅读《Oracle汉字转拼音函数创建与使用指南》,它不仅提供了创建和使用函数的方法,还包含了许多高级技巧和常见问题的解决方案。 参考资源链接:[Oracle汉字转拼音函数创建与使用指南](https://wenku.youkuaiyun.com/doc/647952f0543f844488195f8d?spm=1055.2569.3001.10343)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

六个沃德

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值