# 字符编码

## 位、字节、字符

* 位（bit b）指二进制中的一位，是二进制最小信息单位。   &#x20;
* 字节（Byte B）是计算机信息技术用于计量存储容量的一种计量单位。   &#x20;
* 字符（ Character ）是指计算机中使用的字母、数字、汗字和符号等。

## 字符集和字符编码

* 字符集（Charset）：是一个系统支持的所有抽象字符的集合。
* 字符编码（Character Encoding）：是一套法则，使字符集与计算机之间建立对应关系，就是将字符转换为计算机可以接受的数字代码。就是以二进制的数字来对应字符集的字符。

## 常用字符集

### ASCII

ASCII（American Standard Code for Information Interchange，美国信息交换标准代码）是基于拉丁字母的一套电脑编码系统。

ASCII字符集：主要包括控制字符（回车键、退格、换行键等）；可显示字符（英文大小写字符、阿拉伯数字和西文符号）。

ASCII编码：将ASCII字符集转换为计算机可以接受的数字系统的数的规则。**使用7位（bits）表示一个字符，共128字符。**

> ASCII只是为英语国家的人使用的编码，因为里面只是用到了英语常用的字符

![](https://timgsa.baidu.com/timg?image\&quality=80\&size=b9999_10000\&sec=1505479856914\&di=b49f4569d4c1dd9d03b261f9606820b0\&imgtype=0\&src=http%3A%2F%2Fs1.knowsky.com%2F20170221%2F4fdfzprpy1j41.jpg)

### ISO-8859-1

**ISO-8859-1编码是单字节编码，向下兼容ASCII**，是ASCII 的扩展的一种，其编码范围是0x00-0xFF，0x00-0x7F之间完全和ASCII一致，0x80-0x9F之间是控制字符，0xA0-0xFF之间是文字符号。

此字符集支持部分于**欧洲**使用的语言，包括阿尔巴尼亚语、巴斯克语、布列塔尼语、加泰罗尼亚语、丹麦语、荷兰语、法罗语、弗里西语、加利西亚语、德语、格陵兰语、冰岛语、爱尔兰盖尔语、意大利语、拉丁语、卢森堡语、挪威语、葡萄牙语、里托罗曼斯语、苏格兰盖尔语、西班牙语及瑞典语。

**Latin1是ISO-8859-1的别名，有些环境下写作Latin-1。**

### GB 2312

《信息交换用汉字编码字符集》是由中国国家标准总局1980年发布，1981年5月1日开始实施的一套国家标准，标准号是GB 2312—1980。

GB2312编码适用于汉字处理、汉字通信等系统之间的信息交换，通行于中国大陆；新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312。

**基本集共收入汉字6763个和非汉字图形字符682个。**

GB2312的出现，基本满足了汉字的计算机处理需要，它所收录的汉字已经覆盖中国大陆99.75%的使用频率。**对于人名、古汉语等方面出现的罕用字，GB2312不能处理，**&#x8FD9;导致了后来**GBK及GB 18030**汉字字符集的出现。

[GB2312简体中文编码表](http://www.knowsky.com/resource/gb2312tbl.htm)

### BIG5

Big5是在1984年由台湾13家厂商与台湾地区财团法人信息工业策进会为五大中文套装软&#x4EF6;**（宏碁、神通、佳佳、零壹、大众**）所设计的中文内码，所以就称为Big5中文内码。

大五码是使用繁体中文社群中最常用的电脑汉字字符集标准，共收录13,060个中文字，其中有二字为重覆编码。

### Unicode

总数已经超过了65535，所以2个字节的数字是不够用的。

**Unicode编码系统为表达任意语言的任意字符而设计**。它使用4字节的数字来表达每个字母、符号，或者表意文字(ideograph)。

Unicode 还不断在扩增， 每个新版本插入更多新的字符。直至目前为止的第六版，**Unicode 就已经包含了超过十万个字符。**

**Unicode是字符集，UTF-32/ UTF-16/ UTF-8是三种字符编码方案。**

### Unicode与utf8是什么关系？

Unicode是字符集，UTF-32/ UTF-16/ UTF-8是三种字符编码方案。

## 字符编码

### GB2312编码

[GB2312简体中文编码表](http://www.knowsky.com/resource/gb2312tbl.htm)

如何进行编码？

GB2312一个汉字用两个字节表示。

原则上，两个字节可以表示 256×256＝65536 种不同的符号。

考虑到汉字编码与其它国际通用编码，如ASCII 西文字符编码的关系，我国国家标准局采用了加以修正的两字节汉字编码方案，只用了两个字节的低7位。这个方案可以容纳 128×128=16384 种不同的汉字；

为了与标准ASCII码兼容，每个字节中都不能再用３２个控制功能码和码值为32的空格以及127的操作码。

**所以每个字节只能有94个编 码。这样，双七位实际能够表示的字数是：94×94＝8836个。**

> 注意为了与标准ASCII码兼容，每个字节中都不能再用３２个控制功能码和码值为32的空格（特殊字符位，33个）以及1码值为127的操作码（1个，backspace），所以128-33-1=94，所以仅剩下**94×94＝8836个。94个区，94个位。**

## 区位码、国标码、机内码

### 区位码

这个方阵实际上组成一个有94个区（编号由01到94），每个区有94个位（编号由01到94）的汉字字符集。

一个汉字所在的区号和位号的组合就构成了该汉字的"区位码"。其中，高两位为区号，低两位为位号。**这样区位码可以唯一地确定某一汉字或字符；**

反之，任何一个汉字或符号都对应一个唯一的区位码，没有重码。如“保”字在二维代码表中处于17区第3位，区位码即“1703 ”。

### 国标码

国标码是由区位码稍作转换得到，**是一个汉字国家标准的二进制编码。**

其转换方法为：先将十进制区码和位码转换为十六进制的区码和位码；

这样就得了一个与国标码有一个相对位置差的代码，再**将这个代码的第一个字节和第二个字节分别加上20H，就得到国标码。**

如：“保”字的国标码为3123H，它是经过下面的转换得到的：1703D－>1103H->+20H－>3123H。 （20h就是十进制的32，上文提到了“但为了与标准ASCII码兼容，每个字节中都不能再用32个控制功能码和码值为32的空格以及127的操作码”）

> 知识提示：
>
> * 十六进制中H，就是表示数值是以16进制表示的。
> * O为8进制；
> * D是10进制，一般简略掉；
> * B是二进制。
>
> 解析过程：\
> 1\. 1703 十进制的区位码，分别转为16进制 17十进制 =>11十六进制（11H），03十进制=> 03十六进制 （03H），即为1103H\
> 2\. 再将第一个字节和第二个字节分别加上20H，11H+20H = 31H，03H+20H=23H，即3123H

### 机内码

国标码是汉字信息交换的标准编码，但因其前后字节的最高位为0，与ASCII码发生冲突，如 “保”字，国标码为31H和23H，而西文字符“1”和“#”的SCII也为31H和23H，现假如内存中有两个字节为31H和23H；

这到底是一个汉字，还是两个西文字符“1”和“#”？于是就出现了二义性，显然，国标码是不可能在计算机内部直接采用的。

于是汉字的**机内码**采用变形国标码，其变换方法为：将国标码的每个字节都加上128，即将两个字节的最高位由0改1，其余7位不变，如：由上面我们知道，“保”字的国标码为3123H，前字节为00110001B，后字节为00100011B，高位改1为10110001B和10100011B 即为B1A3H，因此，保字的机内码就是**B1A3**H;。**机内码是一个汉字在计算机内部保存的编码。**

> Unicode与 UCS
>
> 国际标准化组织（ISO）、多语言软件制造商组成的统一码联盟。
>
> unicode，为了世界上大多数文字系统进行整理和编码。
>
> ISO组织也在做同样的事情， ISO开展了 ISO/IEC 10646项目，名字叫“ Universal Multiple-Octet Coded Character Set”，简称UCS。
>
> 后来，双方意识到世界不需要2套通用的字符集，所以双方开始进行整合，到unicode2.0时，unicode的编码和ucs的编码都基本一致。

## unicode编码与UTF

一个字符的Unicode编码是确定的。

但是在实际传输过程中，由于不同系统平台的设计不一致，以及出于节省空间的目的，对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式（Unicode Translation Format，**简称为UTF**）。

**简单说UCS或Unicode只是定义了从0到1114112这些数字各自对应是什么字符，而计算机上是如何实现的就是由UTF决定的。**  <br>

UTF-8、 UTF-16、 UTF-32等都是Unicode的编码实现方式。

图：Unicode中的汉字片段

![](https://1303647163-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LfnT30woYyQ9bJF96Hh%2F-LfnTB5KYnkOx0V3e4EY%2F-LfnTHedjF44sHeljuaT%2FUnicode%E4%B8%AD%E7%9A%84%E6%B1%89%E5%AD%97%E7%89%87%E6%AE%B5.png?generation=1558862988102694\&alt=media)

> 重要：Unicode中的汉字组成并不像GBK一样按照汉语拼音来的，并没有什么明显的规律，所以在UTF-8中，汉字要得到汉字的拼音的话是办不到的，这时候要把他转换成GBK，然后再获取汉字的拼音。

### UTF-8 编码方式（规则）

### ![](https://1303647163-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LfnT30woYyQ9bJF96Hh%2F-LfnTB5KYnkOx0V3e4EY%2F-LfnTHefFeKEeqCyluEC%2FUTF-8%20%E7%BC%96%E7%A0%81%E6%96%B9%E5%BC%8F.png?generation=1558862982292290\&alt=media)

> [UNICODE 区域对照表](http://blog.chinaunix.net/uid-10008293-id-2972268.html)
>
> 通过查看上面链接的区域对照表，中国属于`4E00-9FFF CJK Unified Ideographs 中日韓統一表意文字`
>
> 由上图可见属于0800-FFF中，
>
> UTF-8中汉字到底占几个字节？ 3个字节

### 手工把Unicode转为UTF-8的编码

1. “你好” 的unicode码 “ 20320 ” “ 22909 ”
2. 二进制: 100 111101 100000 , 101 100101 111101
3. 每个字从右往左填充 1110xxxx 10xxxxxx 10xxxxxx
4. 计算器二进制转换为16进制：E4  BD  A0 ,  E5 A5 BD

> 解析：
>
> 工具：[在线进制转换](http://tool.oschina.net/hexconvert)
>
> 1. 通过[Unicdoe【真正的完整码表】对照表（二）汉字Unicode表 ](http://blog.csdn.net/hherima/article/details/9045861)查找到 “你”的16进制为4F60 ，转换成十进制为20320， “好”的位置是十六进制是5960+1D = 597D ，转换成十进制为22909
> 2. “你好” 的unicode码 十进制分别为 “ 20320 ” “ 22909 ”
> 3. 转成二进制为00 111101 100000 , 101 100101 111101，从上图中汉字属于0800-FFF中，UTF-8字节流为`1110xxxx 10xxxxxx 10xxxxxx`
> 4. 每个字从右往左填充 1110xxxx 10xxxxxx 10xxxxxx（就是把xxx的）
>
> 11100100 10111101 10100000 ， 11100101 10100101 10111101\
> 5\. 采用在线转换工具，计算器二进制转换为16进制：E4 BD A0 , E5 A5 BD
>
> 练习把unicode转为UTF-8
>
> “北京” 的unicode码 “21271” , “20140”
>
> 第一步：计算器算出每个字的二进制
>
> 第二步：填充 1110xxxx 10xxxxxx 10xxxxxx
>
> 第三步：计算器二进制转换为16进制
>
> 第四步：16进制编辑填入结果，然后以UTF-8查看

### UTF-16

* UTF-16和上面提到的Unicode本身的编码规范是一致的。
* 所有字符的编码长度为16位，也即2个字节。
* 对ASCII字符来说，存储空间浪费严重。
* 不兼容ASCII。

### UTF的字节序和BOM

UTF-16编码每个字符占用了两个字节，在Macintosh (Mac)机和PC机上，对字节顺序的**理解是不一致的**。避免同一个文件造成错乱如何解决呢？

Unicode规范中推荐的标记字节顺序的方法是**BOM**（ Byte Order Mark ）。

> 在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E，“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”，那么这是“奎”还是“乙”？
>
> Unicode规范中推荐的标记字节顺序的方法就是BOM。

UTF-8不需要BOM来表明字节顺序，但可以用BOM来表明编码方式。

> BOM含义
>
> 开头字节 Charset/encoding
>
> **EF BB BF UTF-8**&#x20;
>
> FE FF UTF-16/UCS-2, little endian(UTF-16LE)
>
> FF FE UTF-16/UCS-2, big endian(UTF-16BE)
>
> FF FE 00 00 UTF-32/UCS-4, little endian.
>
> 00 00 FE FF UTF-32/UCS-4, big-endia

### **总结：**

* 区位码前两个位换成十六进制，然后后两位换成十六进制。
* 国际码=区位码（十六进制）＋2020H
* 机内码=国际码＋8080H
* 01-09区为特殊符号。

16-55区为一级汉字，按拼音排序。

56-87区为二级汉字，按部首/笔画排序。

10-15区 及88-94区则未有编码。

## 资料

[UNICODE与UTF-8的转换详解](http://blog.csdn.net/iefreer/article/details/4836844)
