`

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

阅读更多

                      JAVA编解码

                          ---- 乱码问题

 

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

一、需求:

 

二、过程分析:

 

第一步:java文件编码格式

   文件格式非固定:

第二步:java文件编写保存

第三步:编译成Class文件

第四步:load class文件到JVM

第五步:内存

1java文件中的字符串

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

   详细来看:

   1java文件中的字符串

 

 

  String abc = "abc中国";
		byte[] bytes = abc.getBytes();
		for (byte b : bytes) {
			System.out.print(b+"  ");
		}
		System.out.println();
		String newabc=new String(abc);
		System.out.println(newabc);   
       Java文件编码为UTF-8:
       打印结果:
       97  98  99  -28  -72  -83  -27  -101  -67  
       abc中国
       Java文件编码为GBK:
       打印结果:
       97  98  99  -42  -48  -71  -6  
       abc中国
  注: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(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

 String abc = "abc中国";
		byte[] bytes = abc.getBytes("gbk");
		for (byte b : bytes) {
			System.out.print(b+"  ");
		}
		System.out.println();
		String newabc=new String(abc,"utf-8");
		System.out.println(newabc);   

       打印结果如下:
       97  98  99  -42  -48  -71  -6  
       abc?й?

 

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

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

 

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

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

ReadResourceFromNetWork.java

 

       URL url =null;
		try {
			url = new URL("http://wangshuxiong.jhost.cn/wsx.txt");
			URLConnection urlconnection = url.openConnection();
			InputStream ins = urlconnection.getInputStream();
           int a=0;
			while ((a = ins.read()) != -1) {
		    	System.out.print(a+" ");
			}
		    ins.close();
		} catch (Exception e) {	}

       打印结果:97 98 99 214 208 

       此时当前ReadResourceFromNetWork.java 文件的编码为gbk。改变ReadResourceFromNetWork.java 的编码为utf-8 的时候,我们发现一个现象。打印结果依旧为 :      97 98 99 214 208
       当我们改变wsx.txt 的编码为utf-8 ,内容依旧为“abc中” 不论ReadResourceFromNetWork.java文件的编码是utf-8 还是gbk,那么打印结果都为:
                 97 98 99 228 184 173 
     
		} catch (Exception e) {	}
	      

 

 

由此我们得出一个结论:
       从网络中读取资源文件的时候,无论当前java文件编码为何值,我们最后得到的一个个字节只与读取的资源文件保存的编码有关。
       那么我们可以知道的是:下面wsx.txt 编码为utf-8 的时候,那么读取的字节数组bytes 中的编码为utf-8;
       URL url =null;
		try {
			url = new URL("http://wangshuxiong.jhost.cn/wsx.txt");
			URLConnection urlconnection = url.openConnection();
			InputStream ins = urlconnection.getInputStream();
           byte[] bytes=new byte[ins.available()];
           int len= ins.read(bytes); /*返回的len是保存到bytes数组中实际的长度,比如说bytes数组定义长度为1024,但是只读取了100个字节长度,那么则返回的len为100,len最大值为bytes数组初始长度*/
		    ins.close();
  当前ReadResourceFromNetWork.java编码为gbk的时候;调用下面方法的话肯定会是乱码,前面说过,String newabc=new String(bytes)  等效于String newabc=new String(bytes,Charset.defaultCharset());即为GBK编码,两者编码不统一,乱码是必然,打印结果如下: abc涓?

 

 

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

String newabc=new String(bytesutf-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。所以中文是以三个字节来存储的。Xunicode对应二进制依次填充的

例如: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 ,那么则判断字节是不是在128224范围内,如果是的话,说明是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运行编码进行保存。看事例!

 

 

try {
			FileOutputStream fous = new FileOutputStream(new File("b.txt"));
			fous.write(value.getBytes("gbk"));
			fous.flush();
			fous.close();
		} catch (Exception e) {}
上面的意思是将字符串按照gbk编码生成字节,然后写入到b.txt 文件中。事先,我已经设置了b.txt 的编码为gbk。这样就不会产生错误。因为我们打开b.txt文件看到文字的过程是:解析字节按照b.txt 保存的gbk格式进行解码成对应的unicode再到中文然后存于内存中。
分析可知: 只要写出保存之前的字符串是正确的,那么以何种字节编码写入到文件中都是没有问题的,乱码的关键点在于,打开文件进行查看的过程中是以文件设置的保存编码格式进行转码的。

 

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

<!--EndFragment-->
4
1
分享到:
评论
2 楼 王树雄 2013-05-11  
chairmanMao 写道
楼主解释的真是太详细了,

对大家有用就好
1 楼 chairmanMao 2013-04-28  
楼主解释的真是太详细了,

相关推荐

    java乱码问题解决方法

    Java 开发中常见的问题之一,解决这个问题需要了解 Java 的编码方式、JSP 中文乱码问题、Tomcat 5.5 中文乱码问题、JDBC ODBC Bridge 的 Bug 及其解决方法、Solaris 下 Servlet 编程的中文问题及解决办法等。...

    java字符集编码乱码详解

    ### Java字符集编码乱码详解 #### 一、编码与乱码基础知识 在计算机科学领域,字符集(Character Set)是指一系列符号和电子通信代码的标准集合。每种字符集都有其特定的应用场景和优势。例如,ASCII(American ...

    解析文件中的中文字符串

    Java文件是用Java编程语言编写的源代码文件,它们包含了类定义、方法、变量和可能的字符串常量。C#文件(cs文件)则是C#语言的源代码,同样包含各种程序元素,包括字符串字面量。Designer文件通常与Windows Forms或...

    java中英文字符串截取

    在Java中处理包含中文的字符串时,理解字符编码及其对字符串操作的影响至关重要。通过自定义截取逻辑,可以确保在任何编码下都能正确、完整地截取字符串,避免出现乱码或截断错误。上述代码提供了一个具体的示例,...

    字符串转换为计算公式的若干种方法

    从给定的文件标题“字符串转换为计算公式的若干种方法”及描述“个人收集的几种将字符串表达式转换为计算公式的方法”中,我们可以看出文章主要探讨的是如何将文本形式的数学表达式(字符串)转化为可以进行计算的...

    字符串专题文档

    字符串专题资料中,可能包含以下内容:字符串的基础概念、常用操作方法详解、各种编程语言中的字符串处理差异、正则表达式教程、编码解码原理及实践、字符串在不同场景的应用案例等。通过深入学习这些内容,开发者...

    解决java所有中文乱码集合

    5. 字符串编码解码:在进行字符串的编码与解码操作时,使用getBytes("编码")和new String(字节数组, "解码")方法。 6. 对于Servlet,可以使用request.setCharacterEncoding("UTF-8")来设置请求的编码,response....

    java中文乱码分析

    ### Java中文乱码分析 #### 一、概述 在Java Web开发中,中文乱码问题是一个常见的技术难题,尤其在处理HTTP请求时尤为突出。本文将深入探讨HTTP请求中的中文乱码现象,并提供相应的解决方案。 #### 二、HTTP请求...

    jsp页面中EL表达式被当成字符串处理不显示值问题的解决方法

    通过上述方法,可以有效解决EL表达式被错误地作为字符串处理的问题。但同时,了解EL表达式的另一个新特性也很重要,那就是它作为JSP 2.0中的一个主要新特性,为JSP页面提供了表达语言支持,让访问JSP隐含对象和...

    java编程出现中文乱码解决方法一

    ### Java编程中中文乱码问题解析及解决方案 #### 背景介绍 在Java编程过程中,经常遇到的一个问题是中文字符的显示出现乱码。这主要是因为不同系统、环境或文件编码方式之间的不一致所导致的。例如,在Java Web开发...

    JAVA 解决Properties文件保存中文乱码

    在Java编程中,Properties文件是用于存储配置信息的文本文件,通常包含键值对,其中键和值可以是任意字符串。然而,当这些文件中包含中文字符时,如果不正确地处理编码,可能会出现中文乱码的问题。本文将深入探讨...

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

    例如,以下代码展示了如何使用Gson将包含中文的Java对象转换为JSON字符串,以及如何从JSON字符串反序列化回对象: ```java import com.google.gson.Gson; public class User { private String name; // 构造器、...

    java插入数据乱码解决集锦

    通过上述方法,可以有效解决Java程序在处理中文数据时常见的乱码问题。关键在于确保在整个数据流转过程中所有涉及到的环节都使用一致的字符编码,特别是当涉及到不同系统间的交互时,更要格外注意编码的一致性。

    JavaWeb开发中的中文乱码问题分析及解决方案.docx

    ### JavaWeb开发中的中文乱码问题分析及解决方案 #### 摘要 在JavaWeb开发过程中,中文乱码问题是常见的技术挑战之一。该问题源于不同组件间的编码方式不一致,尤其是在涉及用户输入、数据库交互以及页面展示等环节...

    关于去除字符串前后的空白方法

    `trim()`方法是一种广泛应用于多种编程语言(如Java、C#等)及数据库管理系统(如Oracle、MySQL等)中的函数,用于移除字符串开头或结尾处的空白字符。这里的“空白字符”通常包括空格、制表符、换行符等不可见字符...

    JAVA及相关字符集编码问题

    在Java中,字符串是以Unicode编码存储的,这意味着它可以无缝地处理各种语言的文本。 四、UTF UTF(Unicode Transformation Format)是Unicode的几种实现方式之一,包括UTF-8、UTF-16和UTF-32。其中,UTF-8是最...

    java Web开发乱码解决方案

    ### Java Web 开发中的中文乱码问题及其解决方案 在Java Web开发过程中,中文乱码问题是一种常见的技术难题,尤其在处理客户端与服务器间的数据交互时更为突出。本文将详细介绍Java Web开发中出现乱码的原因,并...

    Java编程中中文乱码问题的研究及解决方案

    总之,解决Java编程中的中文乱码问题,关键在于理解Unicode编码与各种系统之间的转换,并在每个环节正确设置编码。这包括页面编码、请求编码、响应编码以及数据库编码。通过细心的配置和适当地编码转换,可以有效地...

    jdbc连接oracle字符集不同出现乱码

    这是因为 Java 在处理字符串时默认按照特定的字符集进行解码,而在本例中,Java 应用期望使用 `ZHS16GBK` 解码,但实际上却按照 `US7ASCII` 编码的数据进行了解码,从而导致乱码现象的发生。 #### 解决方案 为了...

Global site tag (gtag.js) - Google Analytics