java编解码时的乱码问题

编码格式在java应用中的生命周期:

第一步:java文件编码格式(文件格式非固定)

第二步:java文件编写保存

第三步:编译成Class文件

第四步:load class文件到JVM

第五步:内存

   1java文件中的字符串

   2、运行时从网络中读取到内存中的字符串

   3、运行时从本地文件中读取到内存中的字符串

   4、运行时将内存中的字符串写入到文件中


a.example:编写一个java文件,用来读取网络资源、本地文件a以及java文件中的字符串。然后输出读取的字符串到文件b.txt中。


4bc1a387-7acc-3673-9324-66dded6f7505.png







b.解析生命周期

第一步:java文件编码格式

   文件格式非固定:

    Java文件在编写之前需要指定文件的编码格式,默认编码和当前操作系统平台编码保持一致。比如,当前操作系统平台为windows中文版,那么编码一般为GBK。当然可以对保存文件的编码进行修改。例如修改成UTF-8。那么此时文件保存的编码就为UTF-8


第二步:java文件编写保存

java文件中,有读取网络流的方法、读取本地文件的方法及其输出字符串到文件中的方法;编写完成之后,那么则以第一步的编码进行保存。另外,当前java文件中的所有字符串则以第一步中的编码得以保存。比如说当前java文件中有 String str=abc中国;第一步的编码设置为UTF-8,那么则以UTF-8进行保存。如果是GBK,那么则以GBK进行保存。

  第三步:编译成Class

   编译后的class文件的编码固定为UTF-8;和java文件编码格式无关。说明,编译器在编译的过程中将文件格式做了处理。编译器的这种处理操作不会带来乱码问题,因为我们必须要相信编译器的编解码处理过程。


  第四步:Load class 文件到jvm

   jvm中的所有字符串编码都为unicode。所以的话,从class文件再到jvm。编码又做了一次处理。同样也必须相信这种处理不会为乱码留下伏笔;


  第五步:内存

   内存中运行的是jvm中的数据,jvm中的数据编码为unicode。那么内存中同样也以unicode方式进行存储。但是有一个问题,内存运行的过程中,可能会设计到读取文件内容、网络内容以及输出这些内容的操作。在内存中读取的网络内容、文件内容会以不同的编码出现。这种编码和java文件中处理的方式有关。这儿是乱码问题出现原因的一部分。内存中还有输出字符串内容到文件的操作。这儿也会存在问题。


c.具体实例:


1java文件中的字符串


  1. String abc = "abc中国";  

  2. byte[] bytes = abc.getBytes();  

  3. for (byte b : bytes) {  

  4.    System.out.print(b+"  ");  

  5. }  

  6. System.out.println();  

  7. String newabc=new String(abc);  

  8. System.out.println(newabc);    

  9.     Java文件编码为UTF-8:  

  10.     打印结果:  

  11. 979899  -28  -72  -83  -27  -101  -67

  12.     abc中国  

  13.     Java文件编码为GBK:  

  14.     打印结果:  

  15. 979899  -42  -48  -71  -6

  16.     abc中国  

  17. 注:java文件的编码可以通过选择java文件右键Properties――》Text file encoding 中进行设置  

出现上面打印结果的原因分析如下;

假如当前java文件的编码是gbk,那么"abc中国"则以gbk格式进行保存。

abc.getBytes() 方法将字符串转换成字节处理方式是以当前平台的编码进行处理,而在选择java文件右键Properties――》Text file encoding 中进行设置的编码就是此时java文件平台的编码。

abc.getBytes() 的本质是 abc.getBytes( Charset.defaultCharset())

它们两者是等效的 。因为我们当前的编码设置为gbk,那么就等效于abc.getBytes(gbk)。也就是说abc.getBytes()等价于abc.getBytes(gbk)

同样值得注意的是String newabc=new String(bytes); 这儿也进行了默认操作处理。String newabc=new String(bytes) 等效于String newabc=new String(bytesCharset.defaultCharset()) 因为当前编码是gbk

String newabc=new String(bytes) 等效于 String newabc=new String(bytes,gbk)

   理一下思路:

abc中国gbk编码保存――》以utf-8 编码的class文件存在――》以unicode编码loadjvm中――》同样以unicode的形式存在于内存中――》再以gbk编码转成字节――》最后以gbk编码转成字符串;

因为最后两步字符串转成字节和字节转成字符串的编码是统一的,都为gbk。所以不会有乱码的产生。乱码产生的原因就是最后两步字符串的编解码是不统一的。假如字符串变成字节的过程采用gbk编码,而最后字节变成字符串以utf-8的形式编码。那么肯定会出乱码问题。下面事例就是:


ReadJavaString.java

  1. String abc = "abc中国";  

  2. byte[] bytes = abc.getBytes("gbk");  

  3. for (byte b : bytes) {  

  4.        System.out.print(b+"  ");  

  5.    }  

  6.    System.out.println();  

  7.    String newabc=new String(abc,"utf-8");  

  8.    System.out.println(newabc);    

  9.      打印结果如下:  

  10. 979899  -42  -48  -71  -6

  11.      abc?й?  


 结论:避免乱码出现问题的解决办法就是统一编码。

 字符串――字节     字节――字符串 用同一种编码


2、运行时从网络中读取到内存中的字符串

     假如需求为:在远程服务器中保存着一个编码为gbkwsx.txt 文件,要将wsx.txt 文件中的内容读取到本地进行打印或者存储。wsx.txt 中的内容为abc

ReadResourceFromNetWork.java


Java代码   收藏代码
  1.     URL url =null;  

  2. try {  

  3.    url = new URL("http://wangshuxiong.jhost.cn/wsx.txt");  

  4.    URLConnection urlconnection = url.openConnection();  

  5.    InputStream ins = urlconnection.getInputStream();  

  6. int a=0;  

  7. while ((a = ins.read()) != -1) {  

  8.        System.out.print(a+" ");  

  9.    }  

  10.    ins.close();  

  11. } catch (Exception e) { }  

  12.     打印结果:979899214208

  13.     此时当前ReadResourceFromNetWork.java 文件的编码为gbk。改变ReadResourceFromNetWork.java 的编码为utf-8 的时候,我们发现一个现象。打印结果依旧为 :      979899214208

  14.     当我们改变wsx.txt 的编码为utf-8 ,内容依旧为“abc中” 不论ReadResourceFromNetWork.java文件的编码是utf-8 还是gbk,那么打印结果都为:  

  15. 979899228184173

  16. } catch (Exception e) { }  


  1. 由此我们得出一个结论:  

  2.       从网络中读取资源文件的时候,无论当前java文件编码为何值,我们最后得到的一个个字节只与读取的资源文件保存的编码有关。  

  3.       那么我们可以知道的是:下面wsx.txt 编码为utf-8 的时候,那么读取的字节数组bytes 中的编码为utf-8;  

  4.       URL url =null;  

  5. try {  

  6.            url = new URL("http://wangshuxiong.jhost.cn/wsx.txt");  

  7.            URLConnection urlconnection = url.openConnection();  

  8.            InputStream ins = urlconnection.getInputStream();  

  9. byte[] bytes=newbyte[ins.available()];  

  10. int len= ins.read(bytes); /*返回的len是保存到bytes数组中实际的长度,比如说bytes数组定义长度为1024,但是只读取了100个字节长度,那么则返回的len为100,len最大值为bytes数组初始长度*/

  11.            ins.close();  

  12.  当前ReadResourceFromNetWork.java编码为gbk的时候;调用下面方法的话肯定会是乱码,前面说过,String newabc=new String(bytes)  等效于String newabc=new String(bytes,Charset.defaultCharset());即为GBK编码,两者编码不统一,乱码是必然,打印结果如下: abc涓?  



要解决上面乱码问题,方法很简单,不是说两者统一就行了嘛。既然读取字节的时候无法改变字节读取的编码,事实上也是万万不能改变的。那么我们就改变字节变成字符串时候的编码

String newabc=new String(bytesutf-8);

打印结果如下:

abc中

小结一下:

对于读取网络资源乱码问题,如果能够知道资源的编码格式,那么,只需要在转成字符串的过程中使用这种编码就行。所以,关键问题落在了判断资源文件编码方式是那种。


获取编码具体可以查看 how to get charset from string and file


3、运行时从本地文件中读取到内存中的字符串

和网络中读取的结果完全一样,参考其上!


4、运行时将内存中的字符串写入到文件中

  首先确保,读取到内存中的字符串正确,然后写入的话,一定要确保知道写入文件的保存编码,而不是按照默认的jvm运行编码进行保存。看事例!

  1. try {  

  2.            FileOutputStream fous = new FileOutputStream(new File("b.txt"));  

  3.            fous.write(value.getBytes("gbk"));  

  4.            fous.flush();  

  5.            fous.close();  

  6.        } catch (Exception e) {}  

   上面的意思是将字符串按照gbk编码生成字节,然后写入到b.txt 文件中。事先,我已经设置了b.txt 的编码为gbk。这样就不会产生错误。因为我们打开b.txt文件看到文字的过程是:解析字节按照b.txt 保存的  gbk格式进行解码成对应的unicode再到中文然后存于内存中。  

   分析可知: 只要写出保存之前的字符串是正确的,那么以何种字节编码写入到文件中都是没有问题的,乱码的关键点在于,打开文件进行查看的过程中是以文件设置的保存编码格式进行转码的。  


link to http://1035054540-qq-com.iteye.com/blog/1856060



你可能感兴趣的:(java编解码时的乱码问题)