文档章节

Java中的字符编码

_-Leon-_
 _-Leon-_
发布于 2014/02/16 18:00
字数 2258
阅读 305
收藏 14

在日常开发中,经常会遇到关于编码的问题,让人总有种弄不清的感觉,这次我就整理一下,以便自己复习和大家分享,文中如有错误请及时指正。

##ASCII编码

先说说编码的由来,它的由来再简单不过了,为了让计算机存储字符。大家知道计算机底层的处理只有二进制,也就是0和1。在没有计算机的时候,人们使用8个晶体管来表示状态和动作,怎么表示呢?也就是说,每个晶体管可以亮或者灭,也就是0和1,根据8个晶体管不同的亮灭组合来表示不同的意思。那么根据数学知识可以知道,一共有256种组合方式(2的8次方)。

这256种表示一开始并没有全部被使用完,只使用了一部分(32个,表示各种状态),随着计算机的出现和发展,很快剩下的位置也开始被占用,用来表示空格、标点符号、数字、大小写字符等,一共使用了127个位置,这127个位置所组成的字符对应表,就称为ANSII编码表(American Standard Code for Information Interchange,美国信息互换标准代码)。简单来说,ANSII是最原始的计算机编码表,表示了西方人使用的几乎所有的常用字符。

##扩展字符集

扩展字符集还是ASCII编码表中的,也就是256-127剩下的哪些位置,这些位置存储了一些更加有意思的字符,比如横线、竖线交叉等等,也就是扩展后的ASCII码表

##GB2312编码表

很明显,随着世界上越来越多的人使用计算机,而原本的ASCII编码表已经没有位置了,那么中国就推出了自己的编码表,将6000多个常用汉字编了进去。不过对ASCII编码表做了一些修改:小于127的字符意义与ANSII编码表一样,把扩展字符集全部舍弃,两个大于127的字符连在一起时,就表示一个汉字。这样就可以组合出7000多个汉字,并且还加入了数学符号、罗马希腊的字母、日文等等,这张码表,就是GB2312编码表。简单来说,GB2312编码表就是ANSII编码的中文扩展码表。

##GBK编码

不过很快,这张GB2312编码表也不够用了,毕竟只有7000多个汉字,然后就继续扩展,加入了20000多个汉字和符号,就形成了GBK编码表,GBK包含GB2312的所有编码

##GB18030

接着,少数民族的文字也加入了进来,就形成了GB18030编码表

##DBCS(Double Byte Charecter Set 双字节字符集)

以上关于中文的这些编码表,被统称为DBCS,在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了,占两个字节,而小于127的,占一个字节。

##Unicode

类似中国,全世界的各个国家都开始编写自己语言的码表,这样就形成了一个混乱的编码时代,于是乎,就有人要解决这个问题,这个组织就是ISO (国际标谁化组织)。他们的办法很简单,废除所有现存编码,重新编纂了一套编码,这套编码包含全世界所有的字符,这就是Unicode编码表的由来。为了兼容各种字符,规定,Unicode编码中不论中文英文还是别的什么文,每个字符占用两个字节。但是问题也来了,在制定Unicode编码的时候,没有考虑到跟已有任何一种编码兼容,这使得同一个汉字可能在不同码表表示的不一样,也就是乱码问题的由来。没有一种简单的算术方法可以把文本内容从UNICODE编码和另一种编码进行转换,这种转换必须通过查表来进行。

如前所述,UNICODE 是用两个字节来表示为一个字符,他总共可以组合出65535不同的字符,这大概已经可以覆盖世界上所有文化的符号。如果还不够也没有关系,ISO已经准备了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来。

##UTF-8

什么是UTF-8呢?简单来说,UTF-8是一种Unicode编码表的存储方式和传输标准。它是为了解决网络传输Unicode编码而诞生的。来看一张UTF-8实现Unicode存储的对照表: UTF-8对照表 从图中看出,UTF-8编码实现所占用的字节数是不确定的,1到6位都有可能。其实4个字节就几乎可以表示全世界的字符了,所以5个字节和6个字节的那种表示方法被停用了。

而且ISO也为以后做了准备了,指定了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来。

##中文到底占几个字节?

这个问题经常被问到,我是这样理解的,这个答案要分情况。如果是DBCS那一套编码,那么中文占两个字节,如果是UTF-8编码,中文占3个字节或4个字节,至于到底是3个还是4个,要看你给的中文在上面那张图的哪个区间段中,不同的区间段对应的UTF-8格式不一样,导致了结果的不一致。

到这里就差不多了,至于著名的“联通”乱码问题我这里不多说了,开发平时也用不上,有兴趣的可以度娘。

说了这么多,可能有些混淆了,这里有个问题,Unicode规定每个字符占两个字节,那么UTF-8编码中算几个呢,中文、英文都分别在不同编码下占几个字节?

关于这个问题,还是那句话,要分情况,下面用程序来验证,这是最清楚的方式了:

<!-- lang: java -->
String testStr = "abc";
String testCh = "你";
System.out.println("GBK编码:英文长度" + testStr.getBytes("GBK").length);
System.out.println("GBK编码:中文长度" + testCh.getBytes("GBK").length);
System.out.println("UTF-8编码:英文长度" + testStr.getBytes("UTF-8").length);
System.out.println("UTF-8编码:中文长度" + testCh.getBytes("UTF-8").length);
System.out.println("Unicode编码:英文长度" + testStr.getBytes("Unicode").length);
System.out.println("Unicode编码:中文长度" + testCh.getBytes("Unicode").length);

以下为输入结果: 运行结果 这个结果GBK与UTF-8的没有疑问,Unicode这个结果有点意外,为什么"abc"三个字符会占8位呢?这个是因为如果你指定要用Unicode来编码,Java其实使用的是UTF-16LE来处理,简单来说,就是这是UTF-16编码的结果,那么为什么是8位呢?因为在UTF-16中,不管是中英日美还是什么文,每个字符都占两个字节,那么"abc"就是6个字节,而为了通信,UTF-16需要在字符前面加标识,这个标识叫BOM头,这个不用管,只要知道,要加2个字节,所以6 + 2 = 8,就是8个字节,而中文"你"占2个字节,再加上BOM头,就是4个字节。那么就记住,UTF-16中,每个字符占两个字节,总的字符长度 = 字符个数 * 2 + 2,就可以了。比如中文"你好",在UTF-16编码下占6个字节。

##总结一下:

  • 一个英文字符在DBCS编码体系中,占一个字节,在UTF-8中占1个字节,在UTF-16中占2个字节,但是在UTF-16计算字节数时,要注意,是总的字符数的两倍,再加上BOM头的两个字节。在Unicode表中,占两个字节。Java是基于Unicode编码的,所以其实Java中的字符都是两个字节,但是由于编码实现不一样,如UTF-8,所以要分清楚这几种情况。

  • 一个中文字符在DBCS编码体系总,占两个字节,在UTF-8中占3到4个字节(中文在Unicode表中对应的数字从3个字节的格式开始),在UTF-16中占2个字节,同样在UTF-16计算字节数时,要注意,是总的字符数的两倍,再加上BOM头的两个字节。

© 著作权归作者所有

_-Leon-_
粉丝 12
博文 17
码字总数 34045
作品 0
朝阳
部门经理
私信 提问
加载中

评论(4)

_-Leon-_
_-Leon-_ 博主

引用来自“freesnow”的评论

先纠正一个笔误吧,“ANSII编码”应该是指"ASCII编码"。ANSI编码是指GBK等ASCII扩展编码吧,但是没有ANSII编码。
写的非常好啊~尤其是后面,感觉很有用啊
感谢指出错误,已经纠正!
freesnow
freesnow
先纠正一个笔误吧,“ANSII编码”应该是指"ASCII编码"。ANSI编码是指GBK等ASCII扩展编码吧,但是没有ANSII编码。
写的非常好啊~尤其是后面,感觉很有用啊
_-Leon-_
_-Leon-_ 博主

引用来自“冯先森”的评论

有点乱了。。

写后面的程序示例就明白了,主要是有混乱的发展历史在里面。Unicode是编码表,utf8是实现,但是gbk既是编码表又是实现方式,确实有些乱的,我也是乱了很长时间。
冯先森
冯先森
有点乱了。。
java双字节编码和项目编码格式的理解

在Java中字符仅以一种形式存在,那就是Unicode(不选择任何特定的编码,直接使用他们在字符集中的编号,这是统一的唯一方法)。由于java采用unicode编码,char 在java中占2个字节。2个字节(...

踏破铁鞋无觅处
2018/08/29
1
0
Java中的字符集编码入门(五)

如果你是JVM的设计者,让你来决定JVM中所有字符的表示形式,你会不会允许使用各种编码方式的字符并存? 我想你的答案是不会,如果在内存中的Java字符可以以GB2312,UTF-16,BIG5等各种编码形...

Java开发者
2010/05/05
165
0
[转] Java 平台中的增补字符

作者:Sun Microsystems, Inc. 的 Norbert Lindenberg 和 Masayoshi Okutsu 摘要 本文介绍 Java 平台支持增补字符的方式。增补字符是 Unicode 标准中代码点超出 U+FFFF 的字符,因此它们无法...

红薯
2010/10/27
673
1
这是一个关于maven项目的问题,碰到过很多类似的情况有谁可以帮忙解释下

[INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 1 resource [INFO] [INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ vcm --- [WARNING] F......

海_风
2013/09/18
6.5K
11
MenuItem 显示中文乱码问题解决方案

MenuItem 显示中文乱码问题解决方案 今天在使用Java做系统托盘图标(TrayIcon),需要为其增加一个右键弹出菜单(PopupMenu),在使用菜单项(MenuItem)时,遇到了一个非常痛苦的事情:中文乱码~...

Java编程思想
2013/10/17
518
0

没有更多内容

加载失败,请刷新页面

加载更多

iOS Xcode升级包地址(感谢大神)

下载地址:DeviceSupport

_____1____
8分钟前
3
0
Qt编写自定义控件71-圆弧进度条

一、前言 现在web形式的图表框架非常流行,国产代表就是echart,本人用过几次,三个字屌爆了来形容,非常强大,而且易用性也非常棒,还是开源免费的,使用起来不要太爽,内置的各种图表和仪表...

飞扬青云
8分钟前
2
0
润乾报表与 ActiveReport JS 功能对比

简介 润乾报表是用于报表制作的大型企业级报表软件,核心特点在于开创性地提出了非线性报表数学模型,采用了革命性的多源关联分片、不规则分组、自由格间运算、行列对称等技术,使得复杂报表...

泡泡糖儿
20分钟前
4
0
【1015】LNMP架构二

【1015】LNMP架构二 三、PHP安装 PHP安装和LAMP安装PHP方法有差别,需要开启php-fpm服务 1、下载PHP7至/usr/local/src/ 切换目录:cd /usr/local/src 2、解压缩 tar -jxvf php-7.3.0.tar.gz...

飞翔的竹蜻蜓
54分钟前
4
0
浅谈Visitor访问者模式

一、前言 什么叫访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+对其进行处理就叫作访问,那么...

青衣霓裳
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部