锁定老帖子 主题:一个关于UTF-8字符集的问题
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2004-07-02
言归正传,我的情况是这样的,在一个WEB系统中,我们统一使用UTF-8字符集,包括页面显示和数据库存储(数据库是ORACLE9i)。在页面到JAVA程序我们使用一个FILTER来对REQUEST和RESPONSE设置字符集为UTF-8,然后从JAVA到数据库没有做任何编码的转换,结果插入数据库的中文是一个字占三个字节的。在JAVA代码里把中文字符串拦截下来看确实是一个中文三个字节,代码是:int length = str.getBytes("UTF-8").length; 结果可想而知,本来数据库中定义的VACHAR2(20)是希望可以输入10个中文的,现在最多只能输入6个中文。如果只是这样那情况还比较简单,问题是出现另外一个状况,就是直接通过ORACLE的客户端(我这里用SQLPLUS)插入中文信息的时候,当客户端字符集使用本机默认的也就是GBK,则插入记录的时候也是把一个中文按三个字节存储,这个可以理解,因为客户端跟服务器端的字符集不一致,把客户端字符集也改成UTF-8后,插入中文确是按两字节存储的。那现在我就很郁闷了,难道是因为客户端字符集跟服务器端一致,所以不需要进行转码,所以就按两字节存储?而因为在页面到JAVA程序时经过了一次转码,所以转码UTF-8后的中文就变成三字节方式???这个问题我实在想不懂。 好了,通过ORACLE客户端插入一条中文记录之后(客户端和服务器端都是UTF-8,所以现在可以插10个中文),通过JDBC把这条记录查找出来的时候却报错了,错误代码如下: 引用 java.sql.SQLException: 不能在 UTF8 和 UCS2 之间转换: failUTF8Conv at oracle.jdbc.dbaccess.DBError.throwSqlException(Ljava.lang.String;Ljava.lang.String;I)V(DBError.java:134) at oracle.jdbc.dbaccess.DBError.throwSqlException(ILjava.lang.Object;)V(DBError.java:179) at oracle.jdbc.dbaccess.DBError.check_error(ILjava.lang.Object;)V(DBError.java:1130) at oracle.jdbc.dbaccess.DBConversion.failUTF8Conv()V(DBConversion.java:2261) at oracle.jdbc.dbaccess.DBConversion.utf8BytesToJavaChars([BI[C)I(DBConversion.java:2061) at oracle.jdbc.dbaccess.DBConversion.charBytesToJavaChars([BI[CI)I(DBConversion.java:878) at oracle.jdbc.dbaccess.DBConversion.CHARBytesToJavaChars([BI[C)I(DBConversion.java:807) at oracle.jdbc.ttc7.TTCItem.getChars(S)[C(TTCItem.java:298) at oracle.jdbc.dbaccess.DBDataSetImpl.getCharsItem(II[IS)[C(DBDataSetImpl.java:1493) at oracle.jdbc.driver.OracleStatement.getCharsInternal(ZI[ISI)[C(OracleStatement.java:3355) at oracle.jdbc.driver.OracleStatement.getStringValue(ZI)Ljava.lang.String;(OracleStatement.java:3556) at oracle.jdbc.driver.OracleResultSetImpl.getString(I)Ljava.lang.String;(OracleResultSetImpl.java:434) at oracle.jdbc.driver.OracleResultSet.getString(Ljava.lang.String;)Ljava.lang.String;(OracleResultSet.java:1482) 我现在被这几个问题搞得百思不得其解,也在网上找资料看,希望有碰过这样的问题的同人可以帮我解惑,先谢过大家了。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2004-07-03
谢谢你的回复,是PreparedStatement的setCharacterStream方法是吧?我会尝试一下的。
但我目前在数据库持久层里使用了HIBERNATE,不过也不是所有部分都使用它,但如果要通过JDBC来解决的话,那改动可能会很大,但我会先尝试一下的。 |
|
返回顶楼 | |
发表时间:2004-07-05
我试了一下,好象不行,不知道是不是我写的不对
代码如下 引用 java.io.StringReader reader = new java.io.StringReader(form.getXXX()); pstmt.setCharacterStream(1, reader, form.getXXX().length()); pstmt.execute(); 这个操作跟pstmt.setString(1, form.getXXX())没有任何差别,我使用另外一个Reader再试一次,代码如下 引用 java.io.ByteArrayInputStream in = new java.io.ByteArrayInputStream(form.getXXX().getBytes()); java.io.InputStreamReader reader = new java.io.InputStreamReader(in, "UTF-8"); pstmt.setCharacterStream(1, reader, form.getXXX().length()); pstmt.execute(); 这次插进数据库的是乱码,我在页面输入三个中文,用LENGTHB看一下既不是6位也不是9位,是8位,很奇怪。 |
|
返回顶楼 | |
发表时间:2004-07-08
一共有五层的转码都有可能出问题:
1.JSP文件的ENCODING 2.HTML的POST 3.SERVER端JVM的DEFAULT ENCODING 4.HTTP Resquest 的 GET 5.DB的存储 从你的描述当中我看不出可以排除哪几层的问题 |
|
返回顶楼 | |
发表时间:2004-07-13
xjhuang 写道 一共有五层的转码都有可能出问题:
1.JSP文件的ENCODING 2.HTML的POST 3.SERVER端JVM的DEFAULT ENCODING 4.HTTP Resquest 的 GET 5.DB的存储 从你的描述当中我看不出可以排除哪几层的问题 你说的情况我也考虑过,我上面说了一大通其实说得都是出现的“症状”,然后通过这个症状我想问题会出在什么地方。不过我可能说的比较乱,在这里再整理一下: 1。ORACLE字符集是UTF-8,WEB系统使用字符集是UTF-8,存储和页面显示都没有问题,只是数据库中对一个中文字符用3个BYTE存储。 2。通过ORACLE的客户端(我用SQLPLUS)插入中文记录,客户端字符集是GBK(包括系统字符集和SQLPLUS字符集),症状跟1是一样的。 3。通过ORACLE的客户端(我用SQLPLUS)插入中文记录,客户端字符集是UTF-8(包括系统字符集和SQLPLUS字符集),中文按2个BYTE存储,但JAVA没法读出这些中文,JDBC DRIVER报转码异常。 4。不用JAVA作为ORACLE的客户端,用C++,症状跟1一样,一个中文3个BYTE。 通过第四个例子,应该就可以把你说的1,2,3,4都否认掉了。 另外说一下运行环境: OS是REDHAT9,字符集是GBK ORACLE版本是9.2.0.4,字符集是UTF-8 JDBC DRIVER是ORACLE自身提供的,使用THIN连接 JDK是1.4.2 C++和JAVA都运行在REDHAT9上 |
|
返回顶楼 | |
发表时间:2004-07-14
如果ORACLE的ENCODING是UTF-8, 那一个中文存贮时用3个BYTES表示是正常的. UTF-8是个变长的ENCODING, 很多中文字都是3个BYTES表示的.
所以1,2,4都没有问题 问题在于3, 我感觉存储的时候已经乱码了,读出来的时候自然会抛错.当然,我没有用过SQLPLUS, 不知道到底是那边的问题. |
|
返回顶楼 | |
发表时间:2004-07-15
指出一点,不存在Java系统内部用编码方式的问题。
Java内部String全部用UTF-16存。 编码转化,只存在与两个系统之间传送Stream/Byte的时候,或者Java系统向永久存储读写的时候 |
|
返回顶楼 | |
发表时间:2004-07-15
ByteArrayInputStream 的创建有问题:
java.io.ByteArrayInputStream in = new java.io.ByteArrayInputStream(form.getXXX().getBytes()); 这句话是转成系统缺省的编码各式的流,如果你当前是中文区域,那么就等同于getBytes("GBK"); java.io.InputStreamReader reader = new java.io.InputStreamReader(in, "UTF-8"); pstmt.setCharacterStream(1, reader, form.getXXX().length()); pstmt.execute(); 可想而知,你先转成GBK的流,然后又按照UTF-8读,完全不对了 |
|
返回顶楼 | |
发表时间:2004-07-15
我觉得,Java用setString(), getString()就可以了。
你写个Java程序试试看,用setString(),然后getString() 至于Oracle内部是否用UTF-8存,和Java没有关系。Oracle thin driver应该负责把Java string转成Oracle内部格式。 |
|
返回顶楼 | |
发表时间:2004-07-15
dake 写道 我觉得,Java用setString(), getString()就可以了。
你写个Java程序试试看,用setString(),然后getString() 至于Oracle内部是否用UTF-8存,和Java没有关系。Oracle thin driver应该负责把Java string转成Oracle内部格式。 我之前就是使用setString和getString来做的,数据库的更新操作我基本上是使用HIBERNATE来做的,至于用setCharacterStream只是用作测试而已,会出现三个中文8个BYTE的情况也许就是你说的原因了。 xjhuang 写道 如果ORACLE的ENCODING是UTF-8, 那一个中文存贮时用3个BYTES表示是正常的. UTF-8是个变长的ENCODING, 很多中文字都是3个BYTES表示的. 所以1,2,4都没有问题 问题在于3, 我感觉存储的时候已经乱码了,读出来的时候自然会抛错.当然,我没有用过SQLPLUS, 不知道到底是那边的问题. 我也从一些资料上看到过: 引用 The UTF-8 encoding is a variable-width encoding of Unicode characters. Seven-bit ASCII characters (\u0000-\u007F) are represented in one byte, so they remain untouched by the encoding (i.e., a string of ASCII characters is a legal UTF-8 string). Characters in the range \u0080-\u07FF are represented in two bytes, and characters in the range \u0800-\uFFFF are represented in three bytes. 但是对于问题3,我一直想不明白就是了。 |
|
返回顶楼 | |