一、前言

最近项目中用到招商银行的企业直连功能,通过接口的方式直接调用招商银行的前置机(http://www.cmbchina.com/corporate/firmbank/FirmbankInfo.aspx?guid=d0917853-6256-44ea-b1e2-24b8524042db)而后进行转账和信息的查询。但招商银行的数据是通过gbk格式进行传输的,而我们的系统使用的utf8编码格式,理所当然的就出现了乱码问题。

借此机会,对编码及Java中如何解决乱码进行汇总。

如果只是想要知道如何解决该问题,可以直接阅读4.2。

如果想对此有全面的了解,请接着向下看。

二、基础知识

1.为什么要编码

大家都知道,计算机存储数据是以0、1进行数据的存储,而人类的语言则多种多样,要想让计算机能够理解众多的人类语言,就必须将人类语言“翻译”成计算机能够看懂的语言,这就是为什么要进行编码。编码的目的就是为了让计算机能够理解人类的语言。

2.编码格式是什么?

通俗的讲编码格式就是“翻译”的规则,人类语言与计算机语言的对应规则。由于人类语言众多,编码格式的种类相应的也有多种。

3.为什么会出现乱码问题?

常见的乱码问题就是因为没有使用正确的编码格式进行信息的转换,导致获得的信息看不懂。如同“计算机”这个词,这是中文的表达方式,转换成英文是“computer”,但如果你用日语的转换规则进行翻译就是“コンピュータ”,一个不懂日语的人肯定不知道这表示什么意思,就认为是乱码了。

4.乱码问题解决原则

也就是说,解决乱码问题的一个必要条件是需要对信息的原始编码格式和信息的目标的编码格式都了解。是不是知道信息的原始编码格式和信息的目标的编码格式就一定能解决乱码问题?答案是否定的。有些词在这个语言中有,而在另一种语言中的情况是存在的,对应到计算机的世界中也是如此,为了能够确保“翻译”正确,有时需要引入第三种编码格式作为桥梁。

三、常用编码规格

1.ASCII 码

学过计算机的人都知道 ASCII 码,ASCII 码是美国标准信息交换代码(American Standard Code for Information Interchange)的缩写, 为美国英语通信所设计。它由128个字符组成,包括大小写字母、数字0-9、标点符号、非打印字符(换行符、制表符等4个)以及控制字符(退格、响铃等)组成。 总共有 128 个,用一个字节的低 7 位表示,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。 例如'A'是65,'a'是97。

但是,由于它是针对英语设计的,当处理带有音调标号(形如汉语的拼音)的欧洲文字时就会出现问题。

2.ISO-8859-1

128 个字符显然是不够用的,于是 ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码,它们是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵盖了大多数西欧语言字符,所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。

3.GB2312

它的全称是《信息交换用汉字编码字符集基本集》,它是双字节编码,总的编码范围是 A1-F7,其中从 A1-A9 是符号区,总共包含 682 个符号,从 B0-F7 是汉字区,包含 6763 个汉字:其中一级汉字3755个,二级汉字3008个;同时,GB2312收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。 
GB2312基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。

4.GBK

GBK是汉字编码标准之一,全称《汉字内码扩展规范》(GBK即“国标”、“扩展”汉语拼音的第一个字母,英文名称:Chinese Internal Code Specification)。GBK编码,是在GB2312-80标准基础上的内码扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE(剔除xx7F),共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK 向下与 GB 2312 编码兼容,向上支持 ISO 10646.1 国际标准,是前者向后者过渡过程中的一个承上启下的标准。

5.Unicode

Unicode(Universal Code 统一码)是基于通用字符集(Universal Character Set)的标准来发展, Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。它通过增加一个高字节对ISO Latin-1字符集进行扩展,当这些高字节位为0时,低字节就是ISO Latin-1字符。UNICODE支持欧洲、非洲、中东、亚洲(包括统一标准的东亚象形汉字和韩国象形文字)。但是,UNICODE并没有提供对诸如Braille, Cherokee, Ethiopic, Khmer, Mongolian, Hmong, Tai Lu, Tai Mau文字的支持。同时它也不支持如Ahom, Akkadian, Aramaic, Babylonian Cuneiform, Balti, Brahmi, Etruscan, Hittite, Javanese, Numidian, Old Persian Cuneiform, Syrian之类的古老文字。

事实证明,对可以用ASCII表示的字符使用UNICODE并不高效,因为UNICODE比ASCII占用大一倍的空间,而对ASCII来说高字节的0对他毫无用处。为了解决这个问题,就出现了一些中间格式的字符集,他们被称为通用转换格式,即UTF(Universal Transformation Format)。

unicode编码规范中常用的是utf-16和utf-8。

6.UTF-16

UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。

UTF-16 统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。

7.UTF-8

UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。UTF-8用1到6个字节编码UNICODE字符。如果UNICODE字符由2个字节表示,则编码成UTF-8很可能需要3个字节。而如果UNICODE字符由4个字节表示,则编码成UTF-8可能需要6个字节。用4个或6个字节去编码一个UNICODE字符可能太多了,但很少会遇到那样的UNICODE字符。UTF-8 可以在同一个页面显示中文简体繁体及其它语言(如日文,韩文) 。

实际表示ASCII字符的UNICODE字符,将会编码成1个字节,并且UTF-8表示与ASCII字符表示是一样的。所有其他的UNICODE字符转化成UTF-8将需要至少2个字节。

小结:

对中文字符后面四种编码格式都能处理,GB2312 与 GBK 编码规则类似,但是 GBK 范围更大,它能处理所有汉字字符,所以 GB2312 与 GBK 比较应该选择 GBK。UTF-16 与 UTF-8 都是处理 Unicode 编码,它们的编码规则不太相同,相对来说 UTF-16 编码效率最高,字符到字节相互转换更简单,进行字符串操作也更好。它适合在本地磁盘和内存之间使用,可以进行字符和字节之间快速切换,如 Java 的内存编码就是采用 UTF-16 编码。但是它不适合在网络之间传输,因为网络传输容易损坏字节流,一旦字节流损坏将很难恢复,想比较而言 UTF-8 更适合网络传输,对 ASCII 字符采用单字节存储,另外单个字符损坏也不会影响后面其它字符,在编码效率上介于 GBK 和 UTF-16 之间,所以 UTF-8 在编码效率上和编码安全性上做了平衡,是理想的中文编码方式。

三、Java编码相关知识

1. byte(字节)和char(字符)

两个基本概念

字符:人类使用的记号,抽象意义上的一个符号。比如‘中’、‘x’。

字节:计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。

在Java中byte和char都是基本数据类型:byte(字节)占8位(8 bit),它的值域被定义为-128~127。char(字符)为两个字节(2 bytes)。

在进行数据存储和传输时必然会涉及到字节和字符间的转换。字符和字节的对应关系因编码格式的不同而有所变化,具体见下面的示例。

需要和Java中的byte和char基本数据类型区分开,byte(字节)占8位(8 bit),它的值域被定义为-128~127。char(字符)为两个字节(2 bytes)。char和byte的对应关系是固定的。

Java语言中基本类型所占存储空间的大小是固定的,它们的大小并不像其它大多数语言那么随机器硬件架构的变化而变化。这种所占存存储空间大小的不变性是java程序具有可移值性的原因之一。

2.编码示例

前面讲的都是基础知识,现在用一个示例直观的了解下同一个字符串在不同的编码格式下的编码结果。

 
public static void getBytesTest() {
    String name = "xjp zx";//(前三个字符是最新的主席的名字,但博客园里无法发表,请自行替换成汉字)
    System.out.print("原始字符: ");
    for (char b : name.toCharArray()) {
        System.out.print(b + " ");
    }
    System.out.println();
    try {
        byte[] iso8859 = name.getBytes("ISO-8859-1");
        printByte("ISO-8859-1",iso8859);
 
        byte[] gb2312 = name.getBytes("GB2312");
        printByte("GB2312",gb2312);
 
        byte[] gbk = name.getBytes("GBK");
        printByte("GBK",gbk);
 
        byte[] utf16 = name.getBytes("UTF-16");
        printByte("UTF-16",utf16);
 
        byte[] utf8 = name.getBytes("UTF-8");
        printByte("UTF-8",utf8);
 
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
}
 
public static void printByte(String charsetName,byte[] bytes) {
    System.out.print("编码格式为 " +charsetName +" 时的bytes值: ");
    for (byte b : bytes) {
        System.out.print(b + " ");
    }
    System.out.println();
}
 

代码的默认编码是gbk格式的.程序执行的结果如下:
从执行结果可以看出:
1)英文字母在各种编码格式中均是一个byte,且对应的byte值均相同.
2)ISO-8859-1编码格式下一个字符为一个byte
3)GB2312和GBK的编码结果基本上相同
4)UTF-16编码格式每一个字符均是两个byte
5)UTF-8编码格式下一个中文字符是3个byte,英文字符是1个byte
 

四、Java中编码转换

1.基本原理

通过上面的示例可以知道,同一个中文字符在不同的编码格式下会有不同的byte值,如果想进行正确的转码,就需要得到对应的正确的byte值,这个可以通过String类的getBytes(String charsetName)方法获得。在获得正的byte值后利用String类的String(byte bytes[], String charsetName)构造函数重新生成一个新的String对象即可。

2.转换示例代码

下面的示例描述了将一个gbk编码的字符串转为utf-8格式,再将utf-8格式的字符串转为gbk格式。

public static void doubleTranslate() throws UnsupportedEncodingException {
    String gbk = "业务参考号重复";
    System.out.print("GBK格式下的bytes: ");
    for (byte b : gbk.getBytes("GBK")) {
        System.out.print(b+" ");
    }
    System.out.println();
    System.out.print("UTF-8格式下的bytes: ");
    String utf8 = new String(gbk.getBytes("UTF-8"),"UTF-8"); //转换为UTF-8
    for (byte b : utf8.getBytes("UTF-8")) {
        System.out.print(b+" ");
    }
    System.out.println();
    System.out.println("gbk转为utf-8: "+utf8);
    System.out.println("utf-8转为gbk: "+new String(utf8.getBytes("GBK"),"GBK"));
}
 
代码执行结果为:
 
 

五、总结及进一步的工作

1.小结:

本文首先对编码知识进行了简要介绍,而后通过示例展示了同一个字符串在不同编码格式下的byte值,而后通过示例演示了在Java中如何进行编码格式的转换。

转换成功的前提是获得要转换编码格式的byte值,而后利用String类的构造函数即可。

2.进一步的工作

本文只是说明了怎么做,但是对其背后的实现原理没有进行介绍。知其然,知其所以然,才是好的学习习惯。接下来会另起一篇介绍转码后面的实现机理。

参考资料:

http://www.blogjava.net/liuyz2006/articles/385768.html

http://blog.csdn.net/baijinwen/article/details/1623544

百度百科及wiki百科

计算机编码基础知识及Java中编码转换相关推荐

  1. java 代码名称转换_计算机编码基础知识及Java中编码转换

    1.ASCII 码 学过计算机的人都知道 ASCII 码,ASCII 码是美国标准信息交换代码(American Standard Code for Information Interchange)的 ...

  2. java里面value_「Java基础知识」Java中包含哪些运算符

    原标题:「Java基础知识」Java中包含哪些运算符 在Java中包含的运算符有:算数运算符,逻辑运算符,关系运算符等. 算数运算符也就是我们平时的加减乘除余等操作:在Java中都是将右边的值赋值给左 ...

  3. java里面string什么意思_「Java基础知识」Java中的字符串是什么

    原标题:「Java基础知识」Java中的字符串是什么 字符串顾名思义就是一些字符组合在一起组成的一串数据,称作字符串,在Java中字符串用双引号包围起来,格式为String string = &quo ...

  4. 幼师学计算机的基础知识,浅谈中职幼师专业计算机基础教学

    摘要:中职教育是以培养学生实用型技能为主的教育模式,<计算应用基础>课程具有很强的理论性与实践性,因此中职院校在开展<计算机基础>课程教学时要本着以培养社会实用型专业人才的要求 ...

  5. Java基础知识(JAVA中String、StringBuffer、StringBuilder类的区别)

    java中String.StringBuffer.StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题.现在总结一下,看看他们的不同与相同. 1.可变与不可 ...

  6. JavaSE基础知识(九)--Java中的方法重载(涉及基本类型的重载)

    Java SE 是什么,包括哪些内容(九)? 本文内容参考自Java8标准 Java中的基本类型有一个特性:它能自动从一个"较小"的类型转换成"较大"的类型! ...

  7. 计算机ucs汉字编码,字符编码基础知识 - Unicode,UCS,GBK,GB2312,UTF-8

    字符编码基础知识- Unicode,UCS,GBK,GB2312,UTF-8 最近遇到一个Unicode和UTF-8关系比较的问题,之前在处理中文显示时也遇到过类似的问题,于是花时间学习了一下,在此做 ...

  8. 计算机字符编码基础知识

    计算机字符编码基础知识 由于计算机只能识别0和1,所以字符(文字.符号)需要编码,图像.音频.视频等也需要编码.本文主要介绍字符编码,特别是与机内码相关的内容. 字符的处理涉及到下列三类编码: 输入码 ...

  9. 【JAVA编码专题】深入分析 Java 中的中文编码问题

    http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/ 几种常见的编码格式 为什么要编码 不知道大家有没有想过一个问题,那就是为什么 ...

最新文章

  1. 打包几乎各程序都需要用到的宏命令和使用名空间
  2. android ui布局开发,Android UI设计初步(基本布局)
  3. 你如何检查选择器是否匹配jQuery中的内容? [重复]
  4. POJ 3281_Dining
  5. 跨链Cosmos(8)同构跨链交易流程
  6. 【机器学习】异常检测算法速览(Python代码)
  7. python位运算符_详细介绍Python语言中的按位运算符
  8. windows服务器安装nginx
  9. mysql中where和and的区别
  10. mac下编译hadoop-3.0.3
  11. 在jsp中调用常量类中的属性值
  12. 程序员资源系列(不断完善中)
  13. 西塘游(2007-08-14)
  14. Pycharm中进行Python远程开发
  15. Java枚举类的简单使用
  16. python爆破脚本_snmp爆破(python脚本)
  17. 网络管理软件免费linux,SugarNMSTool免费版
  18. 小米笔记本pro系统重置记事
  19. 如何注册PayPal账户
  20. 好家伙?阿里网盘的分享功能已经出来了?

热门文章

  1. java se与java ee的区别
  2. 集美大学诚毅学院传一科技杯《消灭星星》
  3. hdu 5151 Sit sit sit(区间dp)
  4. 判断一个字符串是否是合法IP地址
  5. 评价CommonPhotoStar(动感相册)软件的特色(真是太棒了)
  6. Linux中的NFS共享
  7. 如何手动开启STFServer.apk
  8. python提供了3个常用的读文件函数、分别是,Python3 对文件操作
  9. 对于基本的捕鱼游戏设计思路(四)——序列帧动画
  10. 基于VS和opencv的烟雾识别