精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-08-17
1、UTF-8用几个字节表示一个汉字? 这各答案你可能了解,但也可能不了解,我敢打保票一半人会不清楚(包括特意查资料之前的我)。 了解这个对编程有什么影响? 以下我把对yoolywu的回答,转为帖子发表,以表重视。 yollywu的问: 引用 系统有两个子系统,一个是BS的,一个是delphi做的CS,中间的数据传输是通过XML进行传输的。在XML传输的功能实现后,要求对XML进行加密解密.加密解密算法是CS端用delphi写的,然后这边用JAVA写个同样的算法。现在碰到的一个问题是:
用该算法的时候,CS和BS各自都能够加解密,我这边的过程是这样的。。。。[但最后]中文始终是乱码 StringBuffer strbuf = new StringBuffer(); try { FileInputStream in = new FileInputStream(file); int size = 0; byte [] buf = new byte[1024]; while ((size=in.read(buf)) != -1) { strbuf.append(new String(buf,0,size)); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return strbuf; Qieqie的答: 以下的代码是错误的: StringBuffer strbuf = new StringBuffer(); ... strbuf.append(new String(buf,0,size)); 第一、 你应该使用ByteArrayOutputStream,将InputStream的字节全部读出来,然后转成byte[]数组,最后在根据你和对方协议规定的字符集合(假设你们规定的是UTF-8,如果没有规定,那么就补充上吧),将byte[]变成String: String theString = new String(bytes, "UTF-8")。 不加"UTF-8"的new String,将使用Java环境设置的字符集,没有特别设置的情况下也就是操作系统的字符集。这是不可靠的。 第二、 不能使用byte[]+StringBuffer:StringBuffer是针对char操作的(String也是)。读取byte时可能刚好把一个多字节的char分成前后两批加入StringBuffer。这样就破坏了char的完整性了。而如果你使用UTF-8编码的中文,你就会中招,导致乱码(其实是因为你的读取是由于byte失去原有顺序导致的,跟一般的乱码还不一样) -- 在UTF-8编码集中,每个汉字使用 3个字符表示! 实践证明: 1、创建一个UTF-8编码的文件:weare.txt 2、写入三个字:“我们是” 3、运行以下代码: public class UTF8 { public static void main(String[] args) throws IOException { String p = "weare.txt"; InputStream in = new FileInputStream(p); int read = in.read(new byte[1204]); System.out.println(read); } } 4、你会发现打印出来的是 9 ! 所以,byte[]+StringBuffer的使用方式是错误的! 不过可以使用StringBuffer + bufferedReader.readLine(),读出一行行后再加入StringBuffer。 或者第2楼说的stringbuffer+reader.read(char[])的形式(毕竟错误是由于byte[]导致的,而非StringBuffer) 参考资料: zh.wikipedia.org 写道 UTF-8 使用一至四个字节为每个字符编码。128 个 ASCII 字符(Unicode 范围由 U+0000 至 U+007F)只需一个字节,带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及马尔代夫语(Unicode 范围由 U+0080 至 U+07FF)需要二个字节,其他基本多文种平面(BMP)中的字符(CJK属于此类-Qieqie注)使用三个字节,其他 Unicode 辅助平面的字符使用四字节编码。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-08-17
对于这个例子,一个简单办法就是用InputStreamReader:
InputStreamReader in = new InputStreamReader(new FileInputStream(file), "UTF-8"); char[] buf ... 读取in到buf,再append到strbuf |
|
返回顶楼 | |
发表时间:2007-08-17
如果是按字节来分批读取,不光是utf-8编码,gbk也是会有问题的,两个汉字是4个字节,而一个英文字母加一个汉字是3个字节,所以不可能每次都刚好从两个字符之间切开,除非所有字符都是汉字,可以按双数来取。
所以必须把字节流全部加载进来,整体一起转换成字符串。 如果事先知道字符编码,那么可以用楼上的简单方法,按字符来读取,读出来的都是完整的字符。 如果事先不知道编码格式,但是像xml文件头中注明了编码格式,那么可以按iso-8859-1的编码格式将读进来的字节流进行字符化,然后根据读到的encoding得到编码格式,按照正确的编码格式重新将读到的内容整体转换成正确的字符串。 因为iso-8859-1格式一个字节对应一个字符,并且每一个字节值都有一个对应的字符,将字符还原到字节时原来的值不会发生改变,所以按字节分批读取也不会破坏数据的完整性。并且gbk和utf-8都是兼容iso-8859-1的,即基本字符的编码是一样的,所以按iso-8859-1编码得到的encoding那一段字符是正确的,可识别的。 也可以只读取一个头部,找到正确的编码格式后,再用InputStreamReader按字符读取。 |
|
返回顶楼 | |
发表时间:2007-08-18
http://www.regexlab.com/zh/encoding.htm
|
|
返回顶楼 | |
发表时间:2007-08-18
想说的一个问题是,协议方过来的是加密后十六进制数的一个大的字符串。文件格式我打开的时候另存默认格式是ANSI.
|
|
返回顶楼 | |
发表时间:2007-08-18
我自己在读取密文的时候用的是这个
InputStreamReader read=null; String line; try { read=new InputStreamReader (new FileInputStream(file),"UTF-8"); BufferedReader fileBuffer=new BufferedReader(read); line=fileBuffer.readLine(); while (line != null){ strbuf.append(line); strbuf.append("\n"); line = fileBuffer.readLine(); } } catch (IOException e4){ e4.printStackTrace(); },按字节读是另外有个经验的人说的,但是我发现两种读法都没能改变结果,所以就没有换过来,我觉的主要问题还真有可能是算法上面,就是说我在debug的时候解密后的串转码UTF-8后,因为始终都有几个汉字不能正常显示。 |
|
返回顶楼 | |
发表时间:2007-08-18
谢谢各位,忙了我几天,一直不敢确定是哪边的错误,刚才被BOSS级人物解决了,他两边都懂,分析结果是那边算法有一处小问题造成的。
|
|
返回顶楼 | |
发表时间:2007-08-18
我还以为utf8中中文是3个字节是基本常识呢,没想到还是有人不清楚。。。faint
|
|
返回顶楼 | |
发表时间:2007-08-18
LS的别ft,现实就是这样。
投入门贴的同学,认为这个知识简单,没有体认到“一个汉字用两个字节表示”的错误的结论几乎根深蒂固,因为根深蒂固,所以需要特别发帖指出。 (是否解决了yollywu的问题或者没解决可能不重要|因为也不是很了解他的问题。只是借yollywu的帖子作为引子而已,yollywu见谅) |
|
返回顶楼 | |
发表时间:2007-08-18
yollywu 写道 想说的一个问题是,协议方过来的是加密后十六进制数的一个大的字符串。文件格式我打开的时候另存默认格式是ANSI.
绕了一圈。被你提醒这一说,问题就不是出在编码上。而是这样: 1、密文单位是字节而非字符,那用什么编码读取都可以,UTF-8/GBK/ISOxxxx都可以,他们是兼容ISOxxxx的 2、既然是字节而非字符,那byte[]+StringBuffer不是问题的错误所在,用它读没有问题(肯定是对方的问题,刚好你们的协调人帮你发现了问题确实如此) 3、但,把密文转为明文后,再读取要用预先指定的字符集读取(此时不能使用byte[]+StringBuffer) |
|
返回顶楼 | |