在上篇博客中LZ阐述了java各个渠道转码的过程,阐述了java在运行过程中那些步骤在进行转码,在这些转码过程中如果一处出现问题就很有可能会产生乱码!下面LZ就讲述java在转码过程中是如何来进行编码和解码操作的。

编码&解码

在上篇博客中LZ阐述了三个渠道的编码转换过程,下面LZ将结束java在那些场合需要进行编码和解码操作,并详序中间的过程,进一步掌握java的编码和解码过程。在java中主要有四个场景需要进行编码解码操作:

1:I/O操作

2:内存

3:数据库

4:javaWeb

下面主要介绍前面两种场景,数据库部分只要设置正确编码格式就不会有什么问题,javaWeb场景过多需要了解URL、get、POST的编码,servlet的解码,所以javaWeb场景下节LZ介绍。

I/O操作

在前面LZ就提过乱码问题无非就是转码过程中编码格式的不统一产生的,比如编码时采用UTF-8,解码采用GBK,但最根本的原因是字符到字节或者字节到字符的转换出问题了,而这中情况的转换最主要的场景就是I/O操作的时候。当然I/O操作主要包括网络I/O(也就是javaWeb)和磁盘I/O。网络I/O下节介绍。

首先我们先看I/O的编码操作。

InputStream为字节输入流的所有类的超类,Reader为读取字符流的抽象类。java读取文件的方式分为按字节流读取和按字符流读取,其中InputStream、Reader是这两种读取方式的超类。

按字节

我们一般都是使用InputStream.read()方法在数据流中读取字节(read()每次都只读取一个字节,效率非常慢,我们一般都是使用read(byte[])),然后保存在一个byte[]数组中,最后转换为String。在我们读取文件时,读取字节的编码取决于文件所使用的编码格式,而在转换为String过程中也会涉及到编码的问题,如果两者之间的编码格式不同可能会出现问题。例如存在一个问题test.txt编码格式为UTF-8,那么通过字节流读取文件时所获得的数据流编码格式就是UTF-8,而我们在转化成String过程中如果不指定编码格式,则默认使用系统编码格式(GBK)来解码操作,由于两者编码格式不一致,那么在构造String过程肯定会产生乱码,如下:

File file = new File("C:\\test.txt");
InputStream input = new FileInputStream(file);
StringBuffer buffer = new StringBuffer();
byte[] bytes = new byte[1024];
for(int n ; (n = input.read(bytes))!=-1 ; ){
buffer.append(new String(bytes,0,n));
}
System.out.println(buffer);

输出结果:锘挎垜鏄?cm

test.txt中的内容为:我是 cm。

要想不出现乱码,在构造String过程中指定编码格式,使得编码解码时两者编码格式保持一致即可:

buffer.append(new String(bytes,0,n,"UTF-8"));

按字符

其实字符流可以看做是一种包装流,它的底层还是采用字节流来读取字节,然后它使用指定的编码方式将读取字节解码为字符。在java中Reader是读取字符流的超类。所以从底层上来看按字节读取文件和按字符读取没什么区别。在读取的时候字符读取每次是读取留个字节,字节流每次读取一个字节。

字节&字符转换

字节转换为字符一定少不了InputStreamReader。API解释如下:InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。 每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。API解释非常清楚,InputStreamReader在底层读取文件时仍然采用字节读取,读取字节后它需要根据一个指定的编码格式来解析为字符,如果没有指定编码格式则采用系统默认编码格式。

String file = "C:\\test.txt";
String charset = "UTF-8";
// 写字符换转成字节流
FileOutputStream outputStream = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset);
try {
writer.write("我是 cm");
} finally {
writer.close();
}
     </span><span style="color: rgb(0,128,0)">//</span><span style="color: rgb(0,128,0)"> 读取字节转换成字符</span>
FileInputStream inputStream = <span style="color: rgb(0,0,255)">new</span><span style="color: rgb(0,0,0)"> FileInputStream(file);
InputStreamReader reader </span>= <span style="color: rgb(0,0,255)">new</span><span style="color: rgb(0,0,0)"> InputStreamReader(
inputStream, charset);
StringBuffer buffer </span>= <span style="color: rgb(0,0,255)">new</span><span style="color: rgb(0,0,0)"> StringBuffer();
</span><span style="color: rgb(0,0,255)">char</span>[] buf = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,0,255)">char</span>[64<span style="color: rgb(0,0,0)">];
</span><span style="color: rgb(0,0,255)">int</span> count = 0<span style="color: rgb(0,0,0)">;
</span><span style="color: rgb(0,0,255)">try</span><span style="color: rgb(0,0,0)"> {
</span><span style="color: rgb(0,0,255)">while</span> ((count = reader.read(buf)) != -1<span style="color: rgb(0,0,0)">) {
buffer.append(buf, </span>0<span style="color: rgb(0,0,0)">, count);
}
} </span><span style="color: rgb(0,0,255)">finally</span><span style="color: rgb(0,0,0)"> {
reader.close();
}
System.out.println(buffer);</span></pre>

内存

首先我们看下面这段简单的代码

String s = "我是 cm";
byte[] bytes = s.getBytes();
String s1 = new String(bytes,"GBK");
String s2 = new String(bytes);

在这段代码中我们看到了三处编码转换过程(一次编码,两次解码)。先看String.getTytes():

public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}

内部调用StringCoding.encode()方法操作:

static byte[] encode(char[] ca, int off, int len) {
String csn = Charset.defaultCharset().name();
try {
// use charset name encode() variant which provides caching.
return encode(csn, ca, off, len);
} catch (UnsupportedEncodingException x) {
warnUnsupportedCharset(csn);
}
try {
return encode("ISO-8859-1", ca, off, len);
} catch (UnsupportedEncodingException x) {
// If this code is hit during VM initialization, MessageUtils is
// the only way we will be able to get any kind of error message.
MessageUtils.err("ISO-8859-1 charset not available: "
+ x.toString());
// If we can not find ISO-8859-1 (a required encoding) then things
// are seriously wrong with the installation.
System.exit(1);
return null;
}
}

encode(char[] paramArrayOfChar, int paramInt1, int paramInt2)方法首先调用系统的默认编码格式,如果没有指定编码格式则默认使用ISO-8859-1编码格式进行编码操作,进一步深入如下:

String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;

同样的方法可以看到new String 的构造函数内部是调用StringCoding.decode()方法:

public String(byte bytes[], int offset, int length, Charset charset) {
if (charset == null)
throw new NullPointerException("charset");
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(charset, bytes, offset, length);
}

decode方法和encode对编码格式的处理是一样的。

对于以上两种情况我们只需要设置统一的编码格式一般都不会产生乱码问题。

编码&编码格式

首先先看看java编码类图[1]

首先根据指定的chart设置ChartSet类,然后根据ChartSet创建ChartSetEncoder对象,最后再调用 CharsetEncoder.encode 对字符串进行编码,不同的编码类型都会对应到一个类中,实际的编码过程是在这些类中完成的。下面时序图展示详细的编码过程:

通过这编码的类图和时序图可以了解编码的详细过程。下面将通过一段简单的代码对ISO-8859-1、GBK、UTF-8编码

public class Test02 {
public static void main(String[] args) throws UnsupportedEncodingException {
String string = "我是 cm";
Test02.printChart(string.toCharArray());
Test02.printChart(string.getBytes("ISO-8859-1"));
Test02.printChart(string.getBytes("GBK"));
Test02.printChart(string.getBytes("UTF-8"));
}
</span><span style="color: rgb(0,128,0)">/**</span><span style="color: rgb(0,128,0)">
* char转换为16进制
</span><span style="color: rgb(0,128,0)">*/</span>
<span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> printChart(<span style="color: rgb(0,0,255)">char</span><span style="color: rgb(0,0,0)">[] chars){
</span><span style="color: rgb(0,0,255)">for</span>(<span style="color: rgb(0,0,255)">int</span> i = 0 ; i &lt; chars.length ; i++<span style="color: rgb(0,0,0)">){
System.out.print(Integer.toHexString(chars[i]) </span>+ &quot; &quot;<span style="color: rgb(0,0,0)">);
}
System.out.println(</span>&quot;&quot;<span style="color: rgb(0,0,0)">);
} </span><span style="color: rgb(0,128,0)">/**</span><span style="color: rgb(0,128,0)">
* byte转换为16进制
</span><span style="color: rgb(0,128,0)">*/</span>
<span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> printChart(<span style="color: rgb(0,0,255)">byte</span><span style="color: rgb(0,0,0)">[] bytes){
</span><span style="color: rgb(0,0,255)">for</span>(<span style="color: rgb(0,0,255)">int</span> i = 0 ; i &lt; bytes.length ; i++<span style="color: rgb(0,0,0)">){
String hex </span>= Integer.toHexString(bytes[i] &amp; 0xFF<span style="color: rgb(0,0,0)">);
</span><span style="color: rgb(0,0,255)">if</span> (hex.length() == 1<span style="color: rgb(0,0,0)">) {
hex </span>= '0' +<span style="color: rgb(0,0,0)"> hex;
}
System.out.print(hex.toUpperCase() </span>+ &quot; &quot;<span style="color: rgb(0,0,0)">);
}
System.out.println(</span>&quot;&quot;<span style="color: rgb(0,0,0)">);
}

}

-------------------------outPut:

6211 662f 20 63 6d

3F 3F 20 63 6D

CE D2 CA C7 20 63 6D

E6 88 91 E6 98 AF 20 63 6D

通过程序我们可以看到“我是 cm”的结果为:

char[]:6211 662f 20 63 6d

ISO-8859-1:3F 3F 20 63 6D

GBK:CE D2 CA C7 20 63 6D

UTF-8:E6 88 91 E6 98 AF 20 63 6D

图如下:

更多&参考文献

对于这两种场景我们只需要设置一致正确的编码一般都不会产生乱码问题,通过LZ上面的阐述对于java编码解码的过程应该会有一个比较清楚的认识。其实在java中产生乱码的主要场景是在javaWeb中,所以LZ下篇博文就来讲解javaWeb中的乱码产生情形。

1、Java 编程技术中汉字问题的分析及解决:http://www.ibm.com/developerworks/cn/java/java_chinese/。


-----原文出自:http://cmsblogs.com/?p=1491,请尊重作者辛勤劳动成果,转载说明出处.

-----个人站点:http://cmsblogs.com

java中文乱码解决之道(五)-----java是如何编码解码的的更多相关文章

  1. java中文乱码解决之道(二)-----字符编码详解:基础知识 + ASCII + GB**

    在上篇博文(java中文乱码解决之道(一)-----认识字符集)中,LZ简单介绍了主流的字符编码,对各种编码都是点到为止,以下LZ将详细阐述字符集.字符编码等基础知识和ASCII.GB的详情. 一.基 ...

  2. java中文乱码解决之道(二)—–字符编码详解:基础知识 + ASCII + GB**

    原文出处:http://cmsblogs.com/?p=1412 在上篇博文(java中文乱码解决之道(一)—–认识字符集)中,LZ简单介绍了主流的字符编码,对各种编码都是点到为止,以下LZ将详细阐述 ...

  3. java中文乱码解决之道(九)-----总结

    乱码,我们前台展示的杀手,可能有些朋友和我的经历一样:遇到乱码先按照自己的经验来解决,如果没有解决就google,运气好一搜就可以解决,运气不好可能够你折腾一番了.LZ之所以写这个系列博客就是因为遇到 ...

  4. java中文乱码解决之道(七)-----JSP页面编码过程

    我们知道JSP页面是需要转换为servlet的,在转换过程中肯定是要进行编码的.在JSP转换为servlet过程中下面一段代码起到至关重要的作用. <%@ page language=" ...

  5. java中文乱码解决之道(七)—–JSP页面编码过程

    我们知道JSP页面是需要转换为servlet的,在转换过程中肯定是要进行编码的.在JSP转换为servlet过程中下面一段代码起到至关重要的作用. <%@ page language=" ...

  6. java中文乱码解决之道(一)-----认识字符集

    沉寂了许久(大概有三个多月了吧),LZ"按捺不住"开始写博了! java编码中的中文问题是一个老生常谈的问题了,每次遇到中文乱码LZ要么是按照以前的经验修改,要么则是baidu.c ...

  7. java中文乱码解决之道(一)—–认识字符集

    原文出处:http://cmsblogs.com/?p=1395 沉寂了许久(大概有三个多月了吧),LZ“按捺不住”开始写博了! java编码中的中文问题是一个老生常谈的问题了,每次遇到中文乱码LZ要 ...

  8. java中文乱码解决之道(五)—–java是如何编码解码的

    原文出处:http://cmsblogs.com/?p=1491 在上篇博客中LZ阐述了java各个渠道转码的过程,阐述了java在运行过程中那些步骤在进行转码,在这些转码过程中如果一处出现问题就很有 ...

  9. java中文乱码解决之道(八)-----解决URL中文乱码问题

    我们主要通过两种形式提交向服务器发送请求:URL.表单.而表单形式一般都不会出现乱码问题,乱码问题主要是在URL上面.通过前面几篇博客的介绍我们知道URL向服务器发送请求编码过程实在是实在太混乱了.不 ...

随机推荐

  1. Python的多类型传值和冗余参数

    多类型传值(向函数中传递元组和字典) 1 向函数中传递元组 def func(x,y): print x+y     调用这个函数的时候,我们只需要传入两个变量就可以了,但是比如我有一个元组t = ( ...

  2. [翻译]通过使用正确的search arguments来提高SQL Server数据库的性能

    原文地址:http://www.sqlpassion.at/archive/2014/04/08/improving-query-performance-by-using-correct-search ...

  3. 虚拟机下CentOS 配置IP地址的三种方法

    1.自动获取IP地址(我不是用的这种方法,不做过多介绍) 虚拟机使用桥接模式,相当于连接到物理机的网络里,物理机网络有DHCP服务器自动分配IP地址. #dhclient 自动获取ip地址命令 #if ...

  4. 微信小程序资料

    作者:初雪链接:https://www.zhihu.com/question/50907897/answer/128494332来源:知乎著作权归作者所有,转载请联系作者获得授权. 1:下载开发者工具 ...

  5. MR操作

    MR操作————Map.Partitioner.Shuffle.Combiners.Reduce 1.Map步骤 1.1 读取输入文件,解析成k-v对,其中每个k-v对调用一次map函数 1.2 写自 ...

  6. jquery 回车事件

    简单地记下jquery实现回车事件,代码如下: 全局: $(document).keydown(function(e){ if(e.keyCode==13){ $(".login-li in ...

  7. U-BOOT分析之:环境变量

    (环境如下:U-BOOT  S3C2440  LINUX) 记录自己的学习过程,如果分析有问题,请帮忙指正. 最近在研究U-BOOT的代码,其中的环境变量个人觉得用处非常大,所以重点学习和分析一下. ...

  8. http错误代码含义中英文对照

    Http错误代码含义中文 概要当用户试图通过 HTTP 或文件传输协议 (FTP) 访问一台正在运行 Internet 信息服务 (IIS) 的服务器上的内容时,IIS 返回一个表示该请求的状态的数字 ...

  9. WPF 实现圆形进度条

    项目中用到圆形进度条,首先就想到使用 ProgressBar 扩展一个,在园子里找到迷途的小榔头给出的思路和部分代码,自己加以实现. 进度小于60显示红色,大于60则显示绿色.效果如下: 基本思路: ...

  10. WebRTC通信流程

    WebRTC是HTML5支持的重要特性之一,有了它,不再需要借助音视频相关的客户端,直接通过浏览器的Web页面就可以实现音视频对聊功能.而且WebRTC项目是开源的,我们可以借助WebRTC源码快速构 ...