在上篇博客中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过程肯定会产生乱码,如下:

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

输出结果:锘挎垜鏄?cm

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

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

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

按字符

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

字节&字符转换

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

  1. String file = "C:\\test.txt";
  2. String charset = "UTF-8";
  3. // 写字符换转成字节流
  4. FileOutputStream outputStream = new FileOutputStream(file);
  5. OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset);
  6. try {
  7. writer.write("我是 cm");
  8. } finally {
  9. writer.close();
  10. }
  11. // 读取字节转换成字符
  12. FileInputStream inputStream = new FileInputStream(file);
  13. InputStreamReader reader = new InputStreamReader(
  14. inputStream, charset);
  15. StringBuffer buffer = new StringBuffer();
  16. char[] buf = new char[64];
  17. int count = 0;
  18. try {
  19. while ((count = reader.read(buf)) != -1) {
  20. buffer.append(buf, 0, count);
  21. }
  22. } finally {
  23. reader.close();
  24. }
  25. System.out.println(buffer);

内存

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

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

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

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

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

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

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

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

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

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

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

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

编码&编码格式

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

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

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

  1. public class Test02 {
  2. public static void main(String[] args) throws UnsupportedEncodingException {
  3. String string = "我是 cm";
  4. Test02.printChart(string.toCharArray());
  5. Test02.printChart(string.getBytes("ISO-8859-1"));
  6. Test02.printChart(string.getBytes("GBK"));
  7. Test02.printChart(string.getBytes("UTF-8"));
  8. }
  9. /**
  10. * char转换为16进制
  11. */
  12. public static void printChart(char[] chars){
  13. for(int i = 0 ; i < chars.length ; i++){
  14. System.out.print(Integer.toHexString(chars[i]) + " ");
  15. }
  16. System.out.println("");
  17. }
  18. /**
  19. * byte转换为16进制
  20. */
  21. public static void printChart(byte[] bytes){
  22. for(int i = 0 ; i < bytes.length ; i++){
  23. String hex = Integer.toHexString(bytes[i] & 0xFF);
  24. if (hex.length() == 1) {
  25. hex = '0' + hex;
  26. }
  27. System.out.print(hex.toUpperCase() + " ");
  28. }
  29. System.out.println("");
  30. }
  31. }
  32. -------------------------outPut:
  33. 6211 662f 20 63 6d
  34. 3F 3F 20 63 6D
  35. CE D2 CA C7 20 63 6D
  36. 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/

java是如何编码解码的的更多相关文章

  1. java中文乱码解决之道(五)-----java是如何编码解码的

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

  2. 理清Java中的编码解码转换

    1.字符集及编码方式 概括:字符编码方式及大端小端 详细:彻底理解字符编码 可以通过Charset.availableCharsets()获取Java支持的字符集,以JDK8为例,得到其支持的字符集: ...

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

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

  4. java web url编码解码问题(下载中文名文件)

    问题描述:需要url直接访问中文名的文件,类似于在地址栏里直接输入http://localhost:8080/example/丽江旅游攻略.doc 来进行文件下载,tomcat的server.xml文 ...

  5. java利用zxing编码解码一维码与二维码

    最近琢磨了一下二维码.一维码的编码.解码方法,感觉google的zxing用起来还是比较方便. 本人原创,欢迎转载,转载请标注原文地址:http://wallimn.iteye.com/blog/20 ...

  6. java中文乱码解决之道(六)-----javaWeb中的编码解码

    在上篇博客中LZ介绍了前面两种场景(IO.内存)中的java编码解码操作,其实在这两种场景中我们只需要在编码解码过程中设置正确的编码解码方式一般而言是不会出现乱码的.对于我们从事java开发的人而言, ...

  7. java中文乱码解决之道(六)—–javaWeb中的编码解码

    在上篇博客中LZ介绍了前面两种场景(IO.内存)中的java编码解码操作,其实在这两种场景中我们只需要在编码解码过程中设置正确的编码解码方式一般而言是不会出现乱码的.对于我们从事java开发的人而言, ...

  8. Java 字符编码(二)Java 中的编解码

    Java 字符编码(二)Java 中的编解码 java.nio.charset 包中提供了一套处理字符编码的工具类,主要有 Charset.CharsetDecoder.CharsetEncoder. ...

  9. 第57节:Java中流的操作以及编码解码

    我的博客: https://huangguangda.cn/ https://huangguangda.github.io/ 前言: 编码解码:编码时将信息从一种形式变成为另一种形式,成为编码.编码为 ...

随机推荐

  1. transient解释

    http://www.cnblogs.com/lanxuezaipiao/p/3369962.html

  2. POJ1734无向图求最小环

    题目:http://poj.org/problem?id=1734 方法有点像floyd.若与k直接相连的 i 和 j 在不经过k的情况下已经连通,则有环. 注意区分直接连接和间接连接. * 路径记录 ...

  3. ML(3.2): NavieBayes R_kalR

    ML3.1 介绍e1071包实施朴素贝叶斯分类的函数,本例使用klaR包中的NaiveBayes函数,因为该函数较之前者增加了两个功能,一个是可以输入先验概率,另一个是在正态分布基础上增加了核平滑密度 ...

  4. SqlBulkCopy(批量复制)使用方法

    SqlBulkCopy提供了一种将数据复制到Sql Server数据库表中高性能的方法.SqlBulkCopy 包含一个方法 WriteToServer,它用来从数据的源复制数据到数据的目的地. Wr ...

  5. 【Spring学习笔记-MVC-1.0】Spring MVC架构介绍

    作者:ssslinppp       1. 核心架构图 2. 核心架构的具体流程步骤 3. 具体的核心开发步骤 4. 常用注解 5. <mvc:annotation-driven>配置 6 ...

  6. puppet 工作原理

    Puppet的工作细节分成如下几个步骤: 1.客户端puppetd调用facter,facter会探测出这台主机的一些变量如主机名.内存大小.IP地址等.然后puppetd把这些信息发送到服务器端. ...

  7. java web程序 jdbc连接数据库错误排查方法

    学习jsp.我遇到了麻烦,我总是看不懂500错误,因为每次都显示整个页面的错误,都是英文 我看不懂,后来,把他弄烦了,我也烦了,比起学习java.那个异常可以很简单的就知道.现在解决 了第一个问题,5 ...

  8. Spring Cloud构建微服务架构(七)消息总线

    先回顾一下,在之前的Spring Cloud Config的介绍中,我们还留了一个悬念:如何实现对配置信息的实时更新.虽然,我们已经能够通过/refresh接口和Git仓库的Web Hook来实现Gi ...

  9. TOM带你玩充电 篇三:15款5号电池横评及选购建议——南孚金霸王小米宜家耐时品胜一个都逃不了

    双鹿电池的几个版本 理论上来说性价比:绿骑士>金骑士>黑骑士>蓝骑士 绿骑士和金骑士都很不错.哪个便宜买哪个. 小米性价比虽然最高,但是超市买不到. 蓝骑士是普通碳性电池,黑骑士是高 ...

  10. OSPF里几个特殊区域(stub、Totally stubby、NSSA、Totally NSSA)总结

    网友总结: 简单的说,就是 stub过滤4,5类lsa,ABR会产生缺省的3类lsa,区域内不能引入外部路由 total stub过滤3,4,5类lsa,ABR会产生缺省的3类lsa,区域内不能引入外 ...