`
carver
  • 浏览: 49320 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java编码/乱码小结

 
阅读更多

经常看到有人写这样的代码:new String(“乱码”.getBytes(“GBK”),”UTF-8”),能写出这种代码其实是对编码原理一点都没有理解,这种写法根本没有任何作用,浪费资源。

 

Java编码涉及到的就两个对象:string和byte,编码涉及的也就两种转换:

1. String转byte,把字符串按照特定的字符集转换为字节流,通常的作法是"helloworld".getBytes("gbk")

2. byte转String,把字节流按照特定的字符集转换为字符串,通常的作法是new String("helloworld", "gbk")

 

Java编码的应用场景一般由以下几种:

1. 服务器与浏览器之间的通讯及展示

2. 本地文件与内存之间的交互

 

服务器与浏览器

对于POST请求

服务器通过响应头的编码指定返回给浏览器的字节流是什么方式编码的,浏览器通过响应头的编码来还原字符串,并展示出来。如果响应头中没有指定编码,在服务器端会用容器的默认编码,在浏览器端则用浏览器的默认编码,一般是操作系统的默认编码,中文系统通常是GB2312。

 

对于GET请求

由于不同的浏览器采用的编码不一样,所以经常会导致服务端读取GET请求的参数时出现乱码问题。根本的原因是,HTTP请求头的编码是给body用的,而URL的编码是无法在HTTP请求的任何参数里面指定,这就导致了对于GET请求,服务端是通过容器预设的编码来反编码URL参数的,由于预设编码是写死的,所以不管你是采用网上很多文章都推荐用UrlEncodingFilter,还是new String(“乱码”.getBytes(“GBK”),”UTF-8”),都是白费。

彻底的解决办法是,对GET请求的参数在浏览器端URL编码两次,第一次编码出来的是英文字符了,第二字编码不管采用何种字符集生成的东西都是一样的了。这样在服务端容器不管预设置的是什么编码反解出来的东西都是一样的,第二次反解则用浏览器指定的编码即可。伪代码如:浏览器端encode(encode("data"));服务器端:decode(decode("data"))

 

本地文件与内存

首先要明确两点:

1. 文件存储的是字节,亦即是说保存字符串为文件的时候必须指定一个字符集,如:FileUtils.writeStringToFile(new File("c:/tmp.txt"), "helloworld", "gbk"),这句代码隐含的含义是先把内存中的字符串按gbk编码为字节流,然后再写到文件中。如果你手中获取到的已经是字节流,那输出文件的时候不需要指定编码的,如FileOutputStream.write(bytes)。

2. Java内存中的字符串都是以unicode编码的,一个字符占用两个字节。当你用FileInputStream打开一个文件时,并不需要指定字符集编码的,因为这时候从文件中读入的都是字节流。但是,当你的程序后继会用的这个打开的文件处理字符时,那必须要指定编码,这也是为什么InputStreamReader这个类需要指定编码,因为它涉及到了把字节流转换为字符串的过程。

 

明白了上面两个流程,那你在写程序的时候只要记住:如果不涉及到string与byte之间的转换,代码里面不应该出现任何的编码操作。Java里面最常用的InpuStream和OutputStream,这两个类在操作的时候都不需要指定编码,因为文件本身是没有编码的,编码只存在于文件里面的内容(字节)。在你读入字节,并需要转换为字符时才需要编码;在你输出字节,并需要把字符转为字节是才需要编码。

 

哦,还有一点,char是字符,char[] chars = string.toCharArray(),所以程序中用到了char,肯定就需要byte转string,那就要记住必须在Reader中指定字符集。

 

最后分享一个案例:把字符串解释为DOM对象

第一种作法是:

InputStream in = new ByteArrayInputStream("<helloworld/>".getBytes("utf-8"));

DocumentBuilder.parse(in);

这种做法其实严重浪费了CPU资源,首先它把字符串转为字节流,然后DOM解释器再把字节流转为字符串。而且这种方式还带入了另外一个问题,DOM在把字节转换为字符时会读取XML第一行的字符集编码,如果XML头部声明的编码和字节流编码不一致,那就杯具了。

 

第二种作法是:

StringReader sr = new StringReader("<helloworld/>");

InputSource is = new InputSource(sr);

DocumentBuilder.parse(is);

这种作法避免了string>byte和byte>string两次转换,性能大大提升。由于没有涉及到字符与字节转换,不需要考虑任何编码问题,即使XML头部声明的编码与XML内容不一致,也没问题。

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics