论坛首页 Java企业应用论坛

一个关于UTF-8字符集的问题

浏览 18043 次
精华帖 (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)


我现在被这几个问题搞得百思不得其解,也在网上找资料看,希望有碰过这样的问题的同人可以帮我解惑,先谢过大家了。
   发表时间:2004-07-03  
谢谢你的回复,是PreparedStatement的setCharacterStream方法是吧?我会尝试一下的。

但我目前在数据库持久层里使用了HIBERNATE,不过也不是所有部分都使用它,但如果要通过JDBC来解决的话,那改动可能会很大,但我会先尝试一下的。
0 请登录后投票
   发表时间: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位,很奇怪。
0 请登录后投票
   发表时间:2004-07-08  
一共有五层的转码都有可能出问题:

1.JSP文件的ENCODING
2.HTML的POST
3.SERVER端JVM的DEFAULT ENCODING
4.HTTP Resquest 的 GET
5.DB的存储

从你的描述当中我看不出可以排除哪几层的问题
0 请登录后投票
   发表时间: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上
0 请登录后投票
   发表时间:2004-07-14  
如果ORACLE的ENCODING是UTF-8, 那一个中文存贮时用3个BYTES表示是正常的. UTF-8是个变长的ENCODING, 很多中文字都是3个BYTES表示的.

所以1,2,4都没有问题

问题在于3, 我感觉存储的时候已经乱码了,读出来的时候自然会抛错.当然,我没有用过SQLPLUS, 不知道到底是那边的问题.
0 请登录后投票
   发表时间:2004-07-15  
指出一点,不存在Java系统内部用编码方式的问题。

Java内部String全部用UTF-16存。

编码转化,只存在与两个系统之间传送Stream/Byte的时候,或者Java系统向永久存储读写的时候
0 请登录后投票
   发表时间: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读,完全不对了
0 请登录后投票
   发表时间:2004-07-15  
我觉得,Java用setString(), getString()就可以了。

你写个Java程序试试看,用setString(),然后getString()

至于Oracle内部是否用UTF-8存,和Java没有关系。Oracle thin driver应该负责把Java string转成Oracle内部格式。
0 请登录后投票
   发表时间: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,我一直想不明白就是了。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics