浅析字符编码

浏览: 116 发布日期: 2017-12-20 分类: java

引言

在网络传输的过程中,经常会使用UTF-8编码,以避免乱码的问题。另外,我们使用文本编辑器编辑文件的时候,也可以选择诸如ANSI、Unicode、UTF-8等多种编码方式。那么,各种编码方式之间有什么区别呢?

ASCII编码

对于计算机来说,所有的文件都是以二进制的格式存储的。所谓编码,就是将二进制位映射到字符集的过程。计算机最早起源于美国,当时他们制定了一套字符编码,将英语字符到二进制位之间的关系,做了统一规定,称为 ASCII码。ASCII码规定,0x20(十进制32)以下的字节码称为“控制字符”,分别规定了特殊的用途;0x20到0x7F的字符称为“打印字符”。ASCII编码只使用一个字节的低7位。

ASCII码表:

非ASCII编码

ASCII码只能用来编码英文字符,各个国家为了表示他们自己的字符,分别扩展了ASCII码。例如汉字编码GB2312、GBK和GB18030,扩展了ASCII码的高位字节,并使用两个字节来进行编码。从ASCII、GB2312、GBK到GB18030,这些编码方式是向下兼容的,但是不同国家之间的字符编码各不相同。为了统一字符编码,ISO (国际标准化组织)发布了俗称“UNICODE”的编码规范。

Unicode

Unicode的学名是“Universal Multiple-Octet Coded Character Set”,简称为UCS,UCS可以看作是"Unicode Character Set"的缩写。

UCS有两种格式:UCS-2(两个字节编码)和UCS-4(四个字节编码),其中UCS-4实际上只用了31位,最高位必须为0。

UCS-4根据最高位为0的最高字节分成2^7=128个group,每个group再根据次高字节分为256个plane,每个plane根据第3个字节分为256行 (rows),每行包含256列(cells)。

group[0]的plane[0]被称为“Basic Multilingual Plane”,即BMP。也就是说UCS-4中高两位字节为0的码位被称作BMP,即:将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。

除了ASCII编码之外,Unicode在制订时没有考虑与任何一种现有的编码方案保持兼容,因此GBK与Unicode在汉字的内码编排上完全是不一样的。

UTF-8和UTF-16

Unicode只是规定了字符的编码方式,UTF-8(UTF-16)是实现Unicode的一种用于传输的编码规范。UTF-8编码规则

  • 使用单字节编码,字节的第一位为0,后面7位为这个符号的Unicode码。因此对于英文字符,UTF-8编码和ASCII码是相同的
  • 使用n字节编码,第一个字节的前n位都为1,第(n+1)位为0,后面(n-1)字节的前两位都为“10”

UCS-2与UTF-8编码对照:

UCS-2编码(16进制) UTF-8编码(二进制)
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx 10xxxxxx
0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx

UTF-16

UTF-16以16位为单元对UCS进行编码,对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。由于实际使用的UCS-2,或者UCS-4的BMP必然小于0x10000,因此可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。

UTF的字节序和BOM

UTF-8以字节为编码单元,因此没有字节序的问题;而UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序,即大端法(Big endian)还是小端法(Little endian)。

Unicode规范中推荐的标记字节顺序的方法是BOM(Byte Order Mark)。在UCS编码中有一个叫做“ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF,而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE",这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。

UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EFBBBF,所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

BOM作为一种标记方式,并不是强制要求的

BOM 编码方式
EF BB BF UTF-8
FF FE UTF-16/UCS-2, Little endian
FE FF UTF-16/UCS-2, Big endian
FF FE 00 00 UTF-32/UCS-4, Little endian
00 00 FE FF UTF-32/UCS-4, Big endian

ANSI

Windows系统中,记事本的编码默认是ANSI,代表系统默认编码,在中文系统中一般是GB系列编码。

JVM采用的改进版的UTF-8格式

与“标准”的区别:

  • “null”字符((char)0)用双字节编码,而不使用单字节,因此改进版的UTF-8格式不会直接出现null值
  • 改进版的UTF-8只使用标准版UTF-8格式所定义的单字节、双字节和三字节格式,JVM不能识别标准版UTF-8定义的四字节格式,而是使用自定义的两个三字节格式来代替,且按照Big-Endian顺序存储

改进版UTF-8编码方式:

字节数 位模式 码点值 范围
单字节 0xxxxxxx(x) x&0x7f 0001 - 007F
双字节 110xxxxx(x) 10xxxxxx(y) ((x&0x1f)<<6) + (y&0x3f) 0080 - 07FF
三字节 1110xxxx(x) 10xxxxxx(y) 10xxxxxx(z) ((x&0xf)<<12) + ((y&0x3f)<<6) + (z&0x3f) 0800 - FFFF
六字节 11101101(u) 1010xxxx(v) 10xxxxxx(w) 11101101(x) 1011xxxx(y) 10xxxxxx(z) 0x10000 + ((v&0xf)<<16) + ((w&0x3f)<<10) + ((y&0xf)<<6) + (z&0x3f) U+FFFF

参考资料:

http://pcedu.pconline.com.cn/empolder/gj/other/0505/616631_1.html

http://blog.csdn.net/baixiaoshi/article/details/40786503

返回顶部