java是如何编码解码的
在上篇博客中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();
- }
- // 读取字节转换成字符
- FileInputStream inputStream = new FileInputStream(file);
- InputStreamReader reader = new InputStreamReader(
- inputStream, charset);
- StringBuffer buffer = new StringBuffer();
- char[] buf = new char[64];
- int count = 0;
- try {
- while ((count = reader.read(buf)) != -1) {
- buffer.append(buf, 0, count);
- }
- } finally {
- reader.close();
- }
- System.out.println(buffer);
内存
首先我们看下面这段简单的代码
- 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"));
- }
- /**
- * char转换为16进制
- */
- public static void printChart(char[] chars){
- for(int i = 0 ; i < chars.length ; i++){
- System.out.print(Integer.toHexString(chars[i]) + " ");
- }
- System.out.println("");
- }
- /**
- * byte转换为16进制
- */
- public static void printChart(byte[] bytes){
- for(int i = 0 ; i < bytes.length ; i++){
- String hex = Integer.toHexString(bytes[i] & 0xFF);
- if (hex.length() == 1) {
- hex = '0' + hex;
- }
- System.out.print(hex.toUpperCase() + " ");
- }
- System.out.println("");
- }
- }
- -------------------------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/。
java是如何编码解码的的更多相关文章
- java中文乱码解决之道(五)-----java是如何编码解码的
在上篇博客中LZ阐述了java各个渠道转码的过程,阐述了java在运行过程中那些步骤在进行转码,在这些转码过程中如果一处出现问题就很有可能会产生乱码!下面LZ就讲述java在转码过程中是如何来进行编码 ...
- 理清Java中的编码解码转换
1.字符集及编码方式 概括:字符编码方式及大端小端 详细:彻底理解字符编码 可以通过Charset.availableCharsets()获取Java支持的字符集,以JDK8为例,得到其支持的字符集: ...
- java中文乱码解决之道(五)—–java是如何编码解码的
原文出处:http://cmsblogs.com/?p=1491 在上篇博客中LZ阐述了java各个渠道转码的过程,阐述了java在运行过程中那些步骤在进行转码,在这些转码过程中如果一处出现问题就很有 ...
- java web url编码解码问题(下载中文名文件)
问题描述:需要url直接访问中文名的文件,类似于在地址栏里直接输入http://localhost:8080/example/丽江旅游攻略.doc 来进行文件下载,tomcat的server.xml文 ...
- java利用zxing编码解码一维码与二维码
最近琢磨了一下二维码.一维码的编码.解码方法,感觉google的zxing用起来还是比较方便. 本人原创,欢迎转载,转载请标注原文地址:http://wallimn.iteye.com/blog/20 ...
- java中文乱码解决之道(六)-----javaWeb中的编码解码
在上篇博客中LZ介绍了前面两种场景(IO.内存)中的java编码解码操作,其实在这两种场景中我们只需要在编码解码过程中设置正确的编码解码方式一般而言是不会出现乱码的.对于我们从事java开发的人而言, ...
- java中文乱码解决之道(六)—–javaWeb中的编码解码
在上篇博客中LZ介绍了前面两种场景(IO.内存)中的java编码解码操作,其实在这两种场景中我们只需要在编码解码过程中设置正确的编码解码方式一般而言是不会出现乱码的.对于我们从事java开发的人而言, ...
- Java 字符编码(二)Java 中的编解码
Java 字符编码(二)Java 中的编解码 java.nio.charset 包中提供了一套处理字符编码的工具类,主要有 Charset.CharsetDecoder.CharsetEncoder. ...
- 第57节:Java中流的操作以及编码解码
我的博客: https://huangguangda.cn/ https://huangguangda.github.io/ 前言: 编码解码:编码时将信息从一种形式变成为另一种形式,成为编码.编码为 ...
随机推荐
- 监控Linux的Steps&Q&A
spolight的下载地址:https://www.quest.com/spotlight-on-windows/ 问题1.sar -u 之后,只有一条记录.这种情况执行一下:sudo sar -d; ...
- Javascript undefined 和 null
Javascript undefined 和 null 虽然 Javascript 一切皆对象,但是类型还是有区别的. undefined 表示 未定义的数据类型. null 表示空对象. 在判断时没 ...
- PHP安全相关的配置(2)
php用越来越多!安全问题更为重要!这里讲解如果安全配置php.ini 安全配置一 (1) 打开php的安全模式 php的安全模式是个非常重要的内嵌的安全机制,能够控制一些php中的函数,比如syst ...
- 使用fiddler进行genymotion安卓虚拟机手机抓包
1.首先先下载fiddler,这个直接百度就有啦. 2.打开fiddler ,可以看到这个界面还是挺帅的: 3.选择Tools - Fiddler Options -Https选项卡将配置设置为如下: ...
- 后台调用前台JS(查看客户端IE版本)
1.前端代码 </form> //注意放在form下面<script> function readRegedit() { var obj = n ...
- 如何更改tomcat7及以上版本内存设置
http://jingyan.baidu.com/article/295430f1c22a940c7e0050fb.html?qq-pf-to=pcqq.c2c 当在tomcat的webapps文件夹 ...
- java 网络编程TCP
客户端 服务端
- my sql 只展示 前10条数据的写法
select * from 表 where 条件 limit 10 这里想看多少条 limit 后面的数字就是多少
- [转]CSKIN 作者分享的图片处理类
本代码来自:http://bbs.cskin.net/forum.php?mod=viewthread&tid=113&fromuid=2446 里面没有我想找的任意角度旋转的方法,代 ...
- 使用postman模拟appium的http请求
Appium是Server,接收http请求,使用Postman模拟请求 1.anyproxy 1.1.安装和运行 #安装 npm i -g anyproxy # 运行anyproxy,端口默认800 ...