个人小站

[资料]字符编码详解,系数ASCII、ANSI、Unicode、UTF-8、UTF-16

字数统计: 3.4k阅读时长: 14 min
2018/06/21

突然间想搞清楚unicode以及utf-8之间的关系,虽说之前有点印象,但还是云里雾里的,于是查了下。简单说就是太混乱,没弄清楚也跑出来写文章的很多,很难有文章能真正全说清楚的。当然我也是按我的理解来写的,不一定都对。

基本知识

字符集 - Character Set
编码 - encoding
代码页 - CodePage
代码页是一个选定字符代码的列表,与字符集其实是差不多的概念,只不过代码页是计算机用来切换编码的概念(从Page来理解)。最早来自IBM,后来被微软,oracle ,SAP等广泛采用。由于最开始每个国家都在搞自己的locale编码(也就是ANSI,随后会有详细解释),导致每个国家都不统一,不兼容,可能导致冲突,所以一个系统在处理文字的时候,必须要告诉计算机你的编码是哪个国家和地区的标准,这种国家和标准的代号(其实就是字符编码格式的代号),微软称为Codepage(代码页),其实这个代码页和字符集编码的意思是一样的。告诉你代码页,本质就是告诉了你编码格式。
但是不同厂家的代码页可能完全不同(这个全看厂家怎么放,没有标准,当然一个公司一般也不会随便改自己的代码页),比如, UTF-8字符编码 在IBM对应的代码页是1208,在微软对应的是65001,在德国的SAP公司对应的是 4110
GBK的在微软的代码页是936,告诉你代码页是936其实和告诉你我编码格式是GBK效果完全相同。那么处理文本的时候就不会有问题,不会去考虑某个代码是显示的韩文还是中文,同样,日文和韩文的代码页就和中文不同,这样就可以避免编码冲突导致计算机不知如何处理的问题。当然用这个也可以很容易的切换语言版本。
内码
指操作系统内部的字符编码,中国人的原创词,可以理解为没有任何组织定义过这个词。
区位码
汉字GB2312编码里的概念,不需要特别理解

ASCII-SBCS

ASCII又称SBCS(单字节字符集)
ASCII(American Standard Code for Information Interchange)美国信息交换标准代码,这是计算机上最早使用的通用的编码方案。
上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。

ASCII码一共规定了128个字符的编码,比如空格”SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。

ASCII 这部标准本身就直接规定了字符和字符编码的方式,所以既是字符集又是编码方案。
无论是ANSI标准编码还是Unicode都包含ASCII,通常的乱码都是由非ASCII字符造成的

ASCII码表

ASCII控制字符

二进制 十进制 十六进制 缩写 可以显示的表示法 名称/意义
0000 0000 0 00 NUL 空字符(Null)
0000 0001 1 01 SOH 标题开始
0000 0010 2 02 STX 本文开始
0000 0011 3 03 ETX 本文结束
0000 0100 4 04 EOT 传输结束
0000 0101 5 05 ENQ 请求
0000 0110 6 06 ACK 确认回应
0000 0111 7 07 BEL 响铃
0000 1000 8 08 BS 退格
0000 1001 9 09 HT 水平定位符号
0000 1010 10 0A LF 换行键
0000 1011 11 0B VT 垂直定位符号
0000 1100 12 0C FF 换页键
0000 1101 13 0D CR 归位键
0000 1110 14 0E SO 取消变换(Shift out)
0000 1111 15 0F SI 启用变换(Shift in)
0001 0000 16 10 DLE 跳出数据通讯
0001 0001 17 11 DC1 设备控制一(XON 启用软件速度控制)
0001 0010 18 12 DC2 设备控制二
0001 0011 19 13 DC3 设备控制三(XOFF 停用软件速度控制)
0001 0100 20 14 DC4 设备控制四
0001 0101 21 15 NAK 确认失败回应
0001 0110 22 16 SYN 同步用暂停
0001 0111 23 17 ETB 区块传输结束
0001 1000 24 18 CAN 取消
0001 1001 25 19 EM 连接介质中断
0001 1010 26 1A SUB 替换
0001 1011 27 1B ESC 跳出
0001 1100 28 1C FS 文件分割符
0001 1101 29 1D GS 组群分隔符
0001 1110 30 1E RS 记录分隔符
0001 1111 31 1F US 单元分隔符
0111 1111 127 7F DEL 删除

ASCII可显示字符

二进制 十进制 十六进制 图形
0010 0000 32 20 (空格)(␠)
0010 0001 33 21 !
0010 0010 34 22
0010 0011 35 23 #
0010 0100 36 24 $
0010 0101 37 25 %
0010 0110 38 26 &
0010 0111 39 27
0010 1000 40 28 (
0010 1001 41 29 )
0010 1010 42 2A *
0010 1011 43 2B +
0010 1100 44 2C ,
0010 1101 45 2D -
0010 1110 46 2E .
0010 1111 47 2F /
0011 0000 48 30 0
0011 0001 49 31 1
0011 0010 50 32 2
0011 0011 51 33 3
0011 0100 52 34 4
0011 0101 53 35 5
0011 0110 54 36 6
0011 0111 55 37 7
0011 1000 56 38 8
0011 1001 57 39 9
0011 1010 58 3A :
0011 1011 59 3B ;
0011 1100 60 3C <
0011 1101 61 3D =
0011 1110 62 3E >
0011 1111 63 3F ?
0100 0000 64 40 @
0100 0001 65 41 A
0100 0010 66 42 B
0100 0011 67 43 C
0100 0100 68 44 D
0100 0101 69 45 E
0100 0110 70 46 F
0100 0111 71 47 G
0100 1000 72 48 H
0100 1001 73 49 I
0100 1010 74 4A J
0100 1011 75 4B K
0100 1100 76 4C L
0100 1101 77 4D M
0100 1110 78 4E N
0100 1111 79 4F O
0101 0000 80 50 P
0101 0001 81 51 Q
0101 0010 82 52 R
0101 0011 83 53 S
0101 0100 84 54 T
0101 0101 85 55 U
0101 0110 86 56 V
0101 0111 87 57 W
0101 1000 88 58 X
0101 1001 89 59 Y
0101 1010 90 5A Z
0101 1011 91 5B [
0101 1100 92 5C \
0101 1101 93 5D ]
0101 1110 94 5E ^
0101 1111 95 5F _
0110 0000 96 60 `
0110 0001 97 61 a
0110 0010 98 62 b
0110 0011 99 63 c
0110 0100 100 64 d
0110 0101 101 65 e
0110 0110 102 66 f
0110 0111 103 67 g
0110 1000 104 68 h
0110 1001 105 69 i
0110 1010 106 6A j
0110 1011 107 6B k
0110 1100 108 6C l
0110 1101 109 6D m
0110 1110 110 6E n
0110 1111 111 6F o
0111 0000 112 70 p
0111 0001 113 71 q
0111 0010 114 72 r
0111 0011 115 73 s
0111 0100 116 74 t
0111 0101 117 75 u
0111 0110 118 76 v
0111 0111 119 77 w
0111 1000 120 78 x
0111 1001 121 79 y
0111 1010 122 7A z
0111 1011 123 7B {
0111 1100 124 7C
0111 1101 125 7D }
0111 1110 126 7E ~

ANSI-DBCS

ANSI,American National Standards Institute
个人感觉理论上来讲其实ANSI不算是一种编码,从缩写可以看出,这是一个标准制定协会。应该是它把各地的locale编码都给记录为相应的标准(遗憾的是搜索不到相应的文章,网上都会非常欣喜的告诉你,有好多种ANSI编码。)wiki上有句话”ANSI is a bit of a misnomer as the behavior does not exactly match the ANSI standards and other codepages can be selected, most recently UTF-8 Unicode.”来源于https://en.wikipedia.org/wiki/Windows_code_page,可惜我英文不是很好。从微软的Code Page Identifiers可以看到如下

  • ANSI/OEM Thai (ISO 8859-11); Thai (Windows)
  • ANSI/OEM Japanese; Japanese (Shift-JIS)
  • ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
  • ANSI/OEM Korean (Unified Hangul Code)
  • ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)

Windows 里说的「ANSI」其实是根据当前 locale 选定具体的编码,比如简中 locale 下是 GBK。把自己这些 code page 称作「ANSI」是 Windows 的臭毛病。通常来讲,GB、BIG5、 Shift-JIS都属于ANSI编码。
DBCS(Double-byte Character Sets)的意思是双字节字符集
also known as an “expanded 8-bit character set”, is an extended single-byte character set (SBCS), implemented as a code page. (DBCS是SBCS的扩展)
在这一方案下,字符的宽度可以是一个字节,也可是两个字节。如果字符的宽度是两个字节,那么它的第一个字节就是一个特殊的“前导字节”,该字节是根据所使用的代码页从某个特定范围选定的。前导字节和“尾字节”合起来指定一个唯一的字符编码。
各个ANSI码(GB、BIG5、 Shift-JIS)是由于早期各个国家搞自己的字符编码造成的,这些国家的编码区间都是重叠的。举个例子,一个ANSI的文本文档从中文环境放到日文环境下就会乱码。

Unicode - UCS - 万国码

通用字符集 - UCS(Universal Character Set)
首先要说明的是Unicode和UCS并不是完全相同的概念

Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。Unicode符号需要字节数不等,从1个字节到4个字节,甚至更多。这里就有个严重的问题,计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。出于节省空间的目的,对Unicode编码的实现方式有所不同。

Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format,简称为UTF)
把带有 BOM 的小端序 UTF-16 称作「Unicode」也是 Windows 的臭毛病。
简单说Windows记事本里的Unicode会误导别人。Unicode本身是没有大端序以及小端序的区别的。

UTF - Unicode/UCS Transformation Format

UTF-8编码字节含义

  • UTF-8编码中的任意字节B,如果B的第一位为0,则B为ASCII码且B独立的表示一个字符;
  • 如果B的第一位为1,第二位为0,则B为一个多字节字符中的后续字节;
  • 如果B的前两位为1,第三位为0,则B为两个字节的字符中的第一个字节;
  • 如果B的前三位为1,第四位为0,则B为三个字节的字符中的第一个字节;
  • 如果B的前四位为1,第五位为0,则B为四个字节的字符中的第一个字节;

因此,对UTF-8编码中的任意字节,根据第一位,可判断是否为ASCII字符;根据前二位,可判断该字节是否为一个字符编码的第一个字节;根据前四位(如果前两位均为1),可确定该字节为字符编码的第一个字节,并且可判断对应的字符由几个字节表示;根据前五位(如果前四位为1),可判断编码是否有错误或数据传输过程中是否有错误。
在ASCII字符较多的情况下,UTF-8比UTF-16更节省空间,而在中文字符较多的情况下(UTF-8一个中文字符3字节,UTF-16仍然是两个)UTF-16占用空间更少,UTF-16、UTF-32不是UTF-8的升级。

字符 UTF-8 UTF-16(LE)
21(00100001) 00 21(00000000 00100001)
E2(11100010) 98(1001100) BA(10111010) 26 3A(0010,0110 00,111010)
E6(11100110) 88(10001000) 91(10010001) 62 11(0110,0010 00,010001)

注:LE为小端序,上表中UTF-16(LE)里的”,”用于标出和UTF-8里对应关系。

可以在这里UTF-8以及这里UTF-16 LE验证

UCS-2、UCS-4与UTF-16、UTF-32

UCS-2对每一个Unicode码位使用2bytes字集(16位bit);
UCS-4对每一个Unicode码位使用4bytes字集(32位bit);
UTF-16可看成是UCS-2的父集。在没有辅助平面字符(surrogate code points)前,UTF-16与UCS-2所指的是同一的意思。但当引入辅助平面字符后,就称为UTF-16了。现在若有软件声称自己支持UCS-2编码,那其实是暗指它不能支持在UTF-16中超过2bytes的字集。对于小于0x10000的UCS码,UTF-16编码就等于UCS码。
UTF-32 原本是 UCS-4 的子集,但JTC1/SC2/WG2声明,所有未来对字符的指定都将会限制在BMP及其14个补充平面。
于是就现状而言,除了 UTF-32 标准包含额外的 Unicode 意涵,UCS-4 和 UTF-32 大体是相同的。

字节顺序标记-BOM

编码 表示(十六进制) 表示(十进制)
UTF-8 EF BB BF 239 187 191
UTF-16(大端序) FE FF 254 255
UTF-16(小端序) FF FE 255 254
UTF-32(大端序) 00 00 FE FF 0 0 254 255
UTF-32(小端序) FF FE 00 00 255 254 0 0
UTF-7 2B 2F 76和以下的一个字节:[ 38 、 39、 2B、2F ] 43 47 118和以下的一个字节:[ 56、57 、43、47 ]
en:UTF-1 F7 64 4C 247 100 76
en:UTF-EBCDIC DD 73 66 73 221 115 102 115
en:Standard Compression Scheme for Unicode 0E FE FF 14 254 255
en:BOCU-1 FB EE 28 及可能跟随着FF 251 238 40 及可能跟随着255
GB-18030 84 31 95 33 132 49 149 51
由于UTF-8没有大端序以及小端序的区别,在Linux等系统中,是不带BOM头的,而微软在记事本的保存中都会默认带上BOM头用于区别ANSI文本文档。

扩展阅读


试着用自己的语句把自己的理解表达出来,但还是有点乱哈

原文作者:ted423

原文链接:http://ted423.github.io/Document/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81/

发表日期:June 21st 2018, 9:53:00 pm

更新日期:June 21st 2018, 9:53:00 pm

版权声明:本站原创内容(一般是语句不通顺的那种)采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可,转载内容以及不带个人观点的分享不在此例,摘抄有Wiki的内容的文章统一根据Wiki采用 CC BY-SA 3.0

CATALOG
  1. 1. 基本知识
  2. 2. ASCII-SBCS
    1. 2.1. ASCII码表
      1. 2.1.1. ASCII控制字符
      2. 2.1.2. ASCII可显示字符
  3. 3. ANSI-DBCS
  4. 4. Unicode - UCS - 万国码
    1. 4.1. UTF - Unicode/UCS Transformation Format
  5. 5. UCS-2、UCS-4与UTF-16、UTF-32
    1. 5.1. 字节顺序标记-BOM
  6. 6. 扩展阅读