`

java各种处理字符串情况乱码原因分析及其解决方法

    博客分类:
  • java
阅读更多

 

                      JAVA编解码

                          ---- 乱码问题

 

---- 通过一个事例进行分析

一、需求:

 

二、过程分析:

 

第一步:java文件编码格式

   文件格式非固定:

第二步:java文件编写保存

第三步:编译成Class文件

第四步:load class文件到JVM

第五步:内存

1、java文件中的字符串

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

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

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

 

 

通过一个事例进行分析:

一、需求:

    IDE:

          Myeclipse 10.0

    需求:

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

 

 

 

 

 

 

 

二、过程分析:

第一步: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文件中处理的方式有关。这儿是乱码问题出现原因的一部分。内存中还有输出字符串内容到文件的操作。这儿也会存在问题。

   详细来看:

   1、java文件中的字符串

 

 

Java代码  收藏代码
  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.      97  98  99  -28  -72  -83  -27  -101  -67    
  12.      abc中国  
  13.      Java文件编码为GBK:  
  14.      打印结果:  
  15.      97  98  99  -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 new abc=new String(bytes); 这儿也进行了默认操作处理。String new abc=new String(bytes) 等效于String newabc=new String(bytes,Charset.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

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.       打印结果如下:  
  11.       97  98  99  -42  -48  -71  -6    
  12.       abc?й?  

 

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

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

 

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

      假如需求为:在远程服务器中保存着一个编码为gbk的 wsx.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.   
  13.      打印结果:97 98 99 214 208   
  14.   
  15.      此时当前ReadResourceFromNetWork.java 文件的编码为gbk。改变ReadResourceFromNetWork.java 的编码为utf-8 的时候,我们发现一个现象。打印结果依旧为 :      97 98 99 214 208  
  16.      当我们改变wsx.txt 的编码为utf-8 ,内容依旧为“abc中” 不论ReadResourceFromNetWork.java文件的编码是utf-8 还是gbk,那么打印结果都为:  
  17.                97 98 99 228 184 173   
  18.      
  19. catch (Exception e) { }  
  20.        

 

 

Java代码  收藏代码
  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=new byte[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(bytes,utf-8);

打印结果如下:

abc中 。 

小结一下:

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

有些文件是由BOMbyte order mark 字节序标记)的,那么我们只需要判断文件的BOM 就行。 比如说UTF-8 BOM 是前三个字节为:-17、-69-65GBK 则前两个字节为 -1  -2所以一个文件含有ROM 的话,我们只需要判断字节序列标示就行。但是往往某些资源文件是没有字节序列标示符的。所以,就得考虑其他的方式解决了。

先看一些补充知识:

 Unicode是字符集,全世界所有通用存在的文字都有一个唯一的标示符。

 它的编码范围是0000-FFFF 。两个字节

 各个国家语言的编码范围参照如下:

 http://baike.baidu.com/view/40801.htm

 中文的编码范围:4e00~9fff  大概有两万多个字。

 Ascii的范围为0-127 

 GBK编码中,一个汉字用两个字节来标示,一个英文字符用一个字节表示,说白了就  是ascii值。

 前一个字节十进制的范围是:128-254 ,第二个字节十进制范围是64-254

 UTF-8编码中:一个汉字三个字节标示 

 第一个字节十进制范围: 224-255 ,第二、三个字节十进制 128-255

 

 GBK 和 unicode的关系是存在一个键值对表保存gbk 十六进制和unicode十六进制的关系。然后通过unicode编码和中文对照关系,则可以通过一个gbk编码得到对应的中文汉字。

  流程如下:

       GBK-Unicode对应表                        Unicode

通过gbk——获得unicode编码值——通过unicode编码值获得中文汉字

 

相关参考:http://www.chi2ko.com/jingyan/gbk2uni.htm

 

UTF-8 和Unicode的转换规则关系如下:

 

Unicode符号范围(十六进制

UTF-8编码方式(二进制)

 0000 - 007F 

0xxxxxxx

 0080 - 07FF

 

110xxxxx   10xxxxxx

 0800 - FFFF

1110xxxx   10xxxxxx    10xxxxxx

 

因为中文的unicode范围为:4e00~9fff。所以中文是以三个字节来存储的。X是unicode对应二进制依次填充的

例如:Unicode 编码FFFF 二进制:1111  1111  1111  1111

填充的UTF-8 为:11101111  10111111  10111111

小结: gbk unicode之间的转换是通过gbk unicode映射表。

       Utf-8 与unicode之间的转换是通过转换规则公式

       所以说,unicode是核心中介。Gbk要转换成utf-8的话,先转成unicode。然后unicode再转换成utf-8;反之亦然。

继续接着讨论怎么判断读取资源文件格式的问题:

1、当读取的字节在0-127范围的话,说明是ascii字符。直接通过 char c=(char) ?转换就行 ;

2、如果字节 大于127 ,那么则判断字节是不是在128到224范围内,如果是的话,说明是GBK编码。因为utf-8的第一个字节范围是224255范围内的。

3、如果不在128224范围内,接着判断第二个字节,如果第二个字节在64128范围内的话,那么则为GBK 编码。因为UTF-8 的第二三个字节范围是128--255.获取这两个字节,转成十六进制,再通过gbk unicode映射表就可以得到unicode值,再通过unicode值就可以得到中文汉字。

4、如果第二个字节依旧不在64128范围内。那么则判断第三个字节。如果第三个字节在0--127范围的话,说明前两个字节为gbk编码。因为gbk编码是两个字节。

5、如果第三个字节大于127的话,说明这三个字节为utf-8编码。然后通过utf-8 unicode的转码规则公式换算成unicode,然后通过unicode得到中文汉字。

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

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

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

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

 

 

Java代码  收藏代码
  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) {}  
  7. 上面的意思是将字符串按照gbk编码生成字节,然后写入到b.txt 文件中。事先,我已经设置了b.txt 的编码为gbk。这样就不会产生错误。因为我们打开b.txt文件看到文字的过程是:解析字节按照b.txt 保存的gbk格式进行解码成对应的unicode再到中文然后存于内存中。  
  8. 分析可知: 只要写出保存之前的字符串是正确的,那么以何种字节编码写入到文件中都是没有问题的,乱码的关键点在于,打开文件进行查看的过程中是以文件设置的保存编码格式进行转码的。  

 

申明,如需转载请注明出处!

<!--EndFragment-->
分享到:
评论

相关推荐

    Json操作及中文乱码解决方案

    本Demo主要针对jquery的$.getJSON操作,返回json, 针对传参出现的中文乱码, json里出现中文乱码。

    jsch文件名中文乱码解决办法

    感谢你的下载,文件说明如下: *jsch的源文件 ...*我写的一个小程序,用于将字符串转化成其他编码格式。如果乱码问题依然没解决,这个类也许能帮你找到你需要更改的编码类型。然后修改那三处地方。

    java 算法

    多线程的世界时钟,显示巴黎,罗马,上海时间, AWT界面,Java日期格式化及其使用例子,几个常用方法,判断字符是否属于中文,异常处理类,去掉字符串中重复的子字符串,将指定byte数组以16进制的形式打印到控制台,...

    JAVA 范例大全 光盘 资源

    实例38 Java字符串与文件的互转 88 实例39 截取带汉字的字符串 91 实例40 替换字符串中的部分字符 92 实例41 Java字符串之密码加密 93 实例42 正则表达式验证字符串 95 第7章 Java常用类 98 实例43 数字的舍入...

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    第4章 字符串处理技术 75 4.1 格式化字符串 76 实例060 把数字格式化为货币字符串 76 实例061 格式化当前日期 77 实例062 货币金额大写格式 78 实例063 String类格式化当前日期 80 实例064 字符串大小写转换 82 实例...

    Java面试宝典2020修订版V1.0.1.doc

    35、MySQL、SqlServer、oracle写出字符存储、字符串转时间 52 36、update语句可以修改结果集中的数据吗? 53 37、oracle如何设置主键自动增长? 53 38、表连接、子查询的区别是什么?它们可以相互转化吗?你倾向于用...

    jsp页面下载类

    支持将特定的字符串或者byte数组以指定的文件名提供下载。 在下载过程中需要提供response对象。并且选择下载形式。 下载处理前先进行check,避免出现错误。 下载处理过程中出错时,会报告错误信息。 ...

    常用编码(Unicode,UTF-8,GBK)转换工具

    用于常用编码转换,包括BREW、JAVA等语言UNICODE字符串定义格式,网页编码,GBK及UTF-8的URL编码等

    Java学习笔记-个人整理的

    {1}Java基础}{17}{chapter.1} {1.1}基本语法}{17}{section.1.1} {1.2}数字表达方式}{17}{section.1.2} {1.3}补码}{19}{section.1.3} {1.3.1}总结}{23}{subsection.1.3.1} {1.4}数据类型}{23}{section.1.4} {...

    php手册PHP5研究室编无乱码版本chm

    String 字符串处理函数 CXLIII. Sybase Functions CXLIV. TCP Wrappers Functions CXLV. Tidy Functions CXLVI. Tokenizer Functions CXLVII. Unicode Functions CXLVIII. URL 函数 CXLIX. Variable 变量...

    [开源]用SWT/JFace实现的放大镜软件jZoomer v1.2.0(附源码)

    Bug修正: ·解决内存溢出Bug ·解决字符串乱码Bug `解决拖拽位置不正常Bug 2. 功能添加: ·添加SystemPropertiesReader类,用于解析系统配置文件system.properties ·完成软件的国际化,...

    php结合md5实现的加密解密方法

    主要介绍了php结合md5实现的加密解密方法,涉及PHP字符串操作及加密解密算法实现技巧,需要的朋友可以参考下

    asp.net知识库

    .NET 2.0中的字符串比较 小试ASP.NET 2.0的兼容性 为 asp.net 2.0 的菜单控件增加 target 属性 ASP.NET 2.0 的内部变化 常见的 ASP.NET 2.0 转换问题和解决方案 Asp.Net2.0无刷新客户端回调 体验.net 2.0 的优雅(1...

    poi最新版本及收集的帮助资料

    //设置中西文结合字符串 row.createCell((short)6).setCellType(HSSFCell.CELL_TYPE_ERROR); //建立错误cell FileOutputStream fileOut = new FileOutputStream("workbook.xls"); wb.write(fileOut); fileOut.close...

    《Android应用开发揭秘》源码

     5.2.4 字符串绘制  5.2.5 图像绘制  5.2.6 图像旋转  5.2.7 图像缩放  5.2.8 图像像素操作  5.2.9 Shader类介绍  5.2.10 双缓冲技术  5.2.11 全屏显示  5.2.12 获得屏幕属性  5.3 动画实现  5.3.1 Tween...

    Android应用开发揭秘

    5.2.4 字符串绘制 5.2.5 图像绘制 5.2.6 图像旋转 5.2.7 图像缩放 5.2.8 图像像素操作 5.2.9 Shader类介绍 5.2.10 双缓冲技术 5.2.11 全屏显示 5.2.12 获得屏幕属性 5.3 动画实现 5.3.1 Tween动画 5.3.2 Frame动画 ...

Global site tag (gtag.js) - Google Analytics