资讯专栏INFORMATION COLUMN

浅析字符编码

荆兆峰 / 2626人阅读

摘要:码表非编码码只能用来编码英文字符,各个国家为了表示他们自己的字符,分别扩展了码。为了统一字符编码,国际标准化组织发布了俗称的编码规范。字符的编码是,所以如果接收者收到以开头的字节流,就知道这是编码了。

引言

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

ASCII编码

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

ASCII码表:

非ASCII编码

ASCII码只能用来编码英文字符,各个国家为了表示他们自己的字符,分别扩展了ASCII码。

ISO8859

欧洲码表,单字节编码,包含一系列标准,如:ISO8859-1(西欧字符集)、ISO8859-2(东欧字符集)等

编码规则:

与 ASCII 相容,所有的低位皆不使用

高位中的前 32 个码位 (0x80 -- 0x9F 或 128--159),保留给扩充定义的 32 个控制码,称为 C1 控制码 (0--31 称为 C0 控制码)

高位中第 33 个码位 (0xA0 或 160),也就是对应 ASCII 中 SP (空格) 的码位,总是代表 Non-breakable space,也就是不准许折行的空格

每个字符集定义至多 95 个字符,其码位都在 0xA1 -- 0xFF 或 161--255

每个字符集收录欧洲某地区的共同常用字符,如:ISO8859-1

GB系列编码

汉字编码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
为什么网络传输时常使用utf8编码

utf8具有较好的容错性; 从utf8字节流任何一个地方截断都可以跳过非法的部分找到下一个字符的开头,如果采用unicode编码,从一个字的中间截断会导致接下来所有的字符解析都是错的,这在不可靠的网络传输中是有利的

utf8是变长的,对于常用字符,只使用一个字节编码,可以有效节省存储空间或带宽

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70869.html

相关文章

  • 浅析PHP的输出缓冲控制

    摘要:默认为是一个回调函数,将脚本的所有输出,用所定义的函数进行处理。在输出缓冲区的回调函数中使用这些函数,会导致未定义的错误。 简介 PHP有输出时,可以用输出控制函数来控制输出 缓冲阶段 ob_start(),将内部缓冲区(buffer)打开。当PHP遇到echo,printf等输出语句时, PHP就会将要输出的数据放入缓冲区(buffer)中,等待输出。而只有当缓冲区满了或者php运行...

    wangjuntytl 评论0 收藏0
  • 浅析微信支付:商户平台代金券或立减优惠开通、指定用户代金券发放、查询等

    摘要:本文是浅析微信支付系列文章的第十四篇,主要讲解在如何开通商户平台的代金券或立减优惠功能,商家向指定用户发送代金券,查询发送记录,代金券信息等。代金券微信支付代金券业务是基于微信支付,为了协助商户方便地实现营销优惠措施。 本文是【浅析微信支付】系列文章的第十四篇,主要讲解在如何开通商户平台的代金券或立减优惠功能,商家向指定用户发送代金券,查询发送记录,代金券信息等。 浅析微信支付系列已...

    neroneroffy 评论0 收藏0
  • Java String 源码浅析

    摘要:如果对象长度不相等,则为假。从后往前,判断类中数组的单个字符是否相等,有不相等则为假。由此可以看出,如果对两个超长的字符串进行比较还是非常费时间的。字符串数值化比较将字符串数值化。 引言 从一段代码说起: public void stringTest(){ String a = a+b+1; String b = ab1; System.out.println(...

    2shou 评论0 收藏0
  • 前端培训-初级阶段-场景实战(2019-06-06)-下载文件&下载进度

    摘要:前端最基础的就是。一个朋友的问题,监测下载进度。这节主要是讲如何使用,以及前端下载的核心操作。下载文件,并显示进度条。下载文件,并显示进度条下载文件上面已经实现了,那我们先说说如何显示进度条。实现代码如下,我们操作成读流,然后统计长度。 前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(H...

    sPeng 评论0 收藏0
  • 前端培训-初级阶段-场景实战(2019-06-06)-下载文件&下载进度

    摘要:前端最基础的就是。一个朋友的问题,监测下载进度。这节主要是讲如何使用,以及前端下载的核心操作。下载文件,并显示进度条。下载文件,并显示进度条下载文件上面已经实现了,那我们先说说如何显示进度条。实现代码如下,我们操作成读流,然后统计长度。 前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(H...

    microelec 评论0 收藏0

发表评论

0条评论

荆兆峰

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<