转自:http://1035054540-qq-com.iteye.com/blog/1856060

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编码load于jvm中——》同样以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. 97  98  99  -42  -48  -71  -6
  11. 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. 打印结果:97 98 99 214 208
  13. 此时当前ReadResourceFromNetWork.java 文件的编码为gbk。改变ReadResourceFromNetWork.java 的编码为utf-8 的时候,我们发现一个现象。打印结果依旧为 :      97 98 99 214 208
  14. 当我们改变wsx.txt 的编码为utf-8 ,内容依旧为“abc中” 不论ReadResourceFromNetWork.java文件的编码是utf-8 还是gbk,那么打印结果都为:
  15. 97 98 99 228 184 173
  16. } catch (Exception e) { }
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中 。

小结一下:

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

有些文件是由BOM(byte order mark 字节序标记)的,那么我们只需要判断文件的BOM 就行。 比如说UTF-8 的BOM 是前三个字节为:-17、-69、-65。GBK 则前两个字节为 -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的第一个字节范围是224到255范围内的。

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

4、如果第二个字节依旧不在64到128范围内。那么则判断第三个字节。如果第三个字节在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. 分析可知: 只要写出保存之前的字符串是正确的,那么以何种字节编码写入到文件中都是没有问题的,乱码的关键点在于,打开文件进行查看的过程中是以文件设置的保存编码格式进行转码的。

JAVA网络传输乱码问题相关推荐

  1. java 网络传输中发送byte[]和接收到的不一致_为什么JAVA对象需要实现序列化?

    序列化是一种用来处理对象流的机制.所谓对象流:就是将对象的内容进行流化.可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间.序列化是为了解决在对对象流进行读写操作时所引发的问题.序列化的 ...

  2. java网络传输压缩数据_Java数据压缩与传输实例Java源码

    [实例简介]Java数据压缩与传输实例Java源码 [实例截图] [核心代码] import java.net.*; import java.io.*; import java.util.zip.GZ ...

  3. java字符串 直接能网络传输吗_atitit.二进制数据无损转字符串网络传输

    atitit.二进制数据无损转字符串网络传输 1. gbk的网络传输问题,为什么gbk不能使用来传输二进制数据 gbk会造成信息丢失 由于有些字符在gbk字符集中找不到对应的字符,所以默认使用编码63 ...

  4. Android网络传输中必用的两个加密算法:MD5 和 RSA (附java完成测试代码)

    MD5和RSA是网络传输中最常用的两个算法,了解这两个算法原理后就能大致知道加密是怎么一回事了.但这两种算法使用环境有差异,刚好互补. 一.MD5算法 首先MD5是不可逆的,只能加密而不能解密.比如明 ...

  5. 让你的对象跑出内存,写入到磁盘或者进行网络传输,一文掌握Java对象序列化

    文章目录 对象序列化是什么 如何让对象可序列化 让对象跑出内存 对象序列化是什么 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络 ...

  6. java上传图片特征码到服务器,记一个Base64编码后经网络传输产生的问题

    问题:机器特征码经过网络传输之后,'+'都变成了' ' 详情:机器特征码提取了机器cpuId和mac地址信息,最后经过Base64编码后生成的字符串,一开始我使用的是 String strs = ne ...

  7. java网络爬虫,乱码问题终于完美解决

    java网络爬虫,乱码问题终于完美解决 参考文章: (1)java网络爬虫,乱码问题终于完美解决 (2)https://www.cnblogs.com/-LilyBlog-/p/7593841.htm ...

  8. Java 文件传输小工具,网络传输文件,内网传输

    文件传输小工具,网络传输文件,内网传输 换了一台电脑,想把一台电脑的资料放到另一太电脑上,由于没有太大的U盘就动手用Java写了个小工具.有需要的朋友下载使用! 运行环境 运行环境需要安装JDK,并配 ...

  9. Java DICOM 网络传输_DICOM医学图像处理:fo-dicom网络传输之 C-Echo and C-Store

    背景: 上一篇博文对DICOM中的网络传输进行了介绍,主要参照DCMTK Wiki中的英文原文.通过对比DCMTK与fo-dicom两个开源库对DICOM标准的具体实现,对理解DICOM标准有一个更直 ...

最新文章

  1. 陷阱~关于引用类型,请不要模棱两可!
  2. Codeforces 494D Upgrading Array
  3. golang 执行命令 设置超时
  4. oracle适配器接口,Oracle
  5. python中dataframe合并列名日期到季度_python 处理dataframe中的时间字段方法
  6. 255.0.0.0子网掩码相应的cidr前缀表示法是?_六十四、前缀,后缀,中缀表达式转化求值问题...
  7. php pathinfo()函数
  8. Quartz入门指南
  9. 协程(Python)
  10. MYSQL:RELPACE用法
  11. Undefined exploded archive location(在myeclipse中TOMCAT不能发布程序。)
  12. 会计计算机学什么软件有哪些,会计学习软件
  13. [渝粤教育] 天水师范学院 地理信息系统原理与方法 参考 资料
  14. 阜师院2016年c语言真题答案,C语言A卷答案.doc
  15. 用户画像标签数据开发之标签权重计算
  16. 快消巨头与“饿了么”的数字革命
  17. 二进制与十进制转换器
  18. 今天不聊技术,聊聊如何成为一个靠谱的软件从业人员
  19. Rsutdio安装REmap包出现错误及解决办法
  20. nop sled 空操作雪橇

热门文章

  1. 基于tornado实现web camera
  2. 《Effective C艹》读书笔记(3)
  3. 图像处理(6)--图像深度
  4. 粒子群算法及其Python实现
  5. Keil5打开工程时,弹窗提示not found device
  6. 6. 聚类算法之K-Means
  7. 【C语言】循环结构常见问题1
  8. ChatGPT检测器(Detector)
  9. 华清远见荣获“华为云精英服务商”资质,助推“华为开发者创新中心”项目落地高校
  10. 重磅综述:精准精神病学机器学习的现代观点