编码字符集


常石磊-老爸

character_encoding_yu.png

编码

对字符和图形进行编码是为了计算机可以对图形字符信息的处理、交换、存储、传输、显现、输入和输出。

编码种类

ASCII码

ASCII(American Standard Code for Information Interchange)码在1950年由美国制定,用00~7F表示英文字符。ASCII是事实上的世界标准,后来的字符集基本都兼容ASCII。
ASCII码

ISO-8859-1

ISO-8859-1(Latin alphabet part 1)编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。
此字符集支持部分于欧洲使用的语言。
法语及芬兰语本来也使用ISO/IEC 8859-1来表示。但因它没有法语使用的 œ、Œ、Ÿ 三个字母及芬兰语使用的 Š、š、Ž、ž ,故于1998年被ISO/IEC 8859-15所取代。(ISO 8859-15同时加入了欧元符号)。

GB2312

为了在计算机表示汉字,对ASCII进行了扩展,由于ASCII一个字节的最高位都是0,为了兼容ASCII码,规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(高字节)从0xA1用到 0xFE,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出(FE-A1+1)(FE-A1+1)=8836个字符了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII码里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的“全角”字符,而原来在127号以下的那些就叫“半角”字符了。 这种汉字方案叫做 “GB2312“。GB2312全称为《信息交换用汉字编码字符集》,由中国国家标准总局1980年发布,1981年5月1日开始实施的一套国家标准,标准号是GB2312—1980。GB2312 是对 ASCII 的中文扩展。
GB2312-1980一共收录了7445个字符,包括6763个汉字和682个其它符号,覆盖了99.75%的常用简体汉子。GB2312编码范围:A1A1-FEFE,其中汉字编码范围:B0A1-F7FE。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72
94=6768。其中有5个空位是D7FA-D7FE。
GB2312虽然已经由兼容它的GBK和GB18020取代,但是还是被广泛使用。

凡是GB开头的国标都是强制标准,GB/T 是推荐标准,不强制使用。

BIG5

BIG5是在港澳台地区广泛使用的繁体汉字字符集,俗称“大五码”,其由5家台湾公司共同开发,所有叫BIG5。
1983年10月,台湾国家科学委员会、教育部国语推行委员会、中央标准局、行政院共同制定了《通用汉字标准交换码》,后经修订于1992年5月公布,更名为《中文标准交换码》,BIG5是台湾资讯工业策进会根据以上标准制定的编码方案。2003年,Big5被收录到台湾官方标准的附录当中,取得了较正式的地位。这个最新版本被称为Big5-2003。
BIG5是双字符编码,两个字节表示一个汉字。高位字节使用81-FE,低位字节使用40-7E,A1-FE。
BIG-5 码收录13468个符号和汉字,符号408个,汉字13060个,分为常用字和次常用字两部分,各部分中的汉字按照笔画/部首排列。其中:常用字5401个,次常用字7659。

GBK

GB2312-1980仅收汉字6763个,这大大少于现有汉字,随着时间推移及汉字文化的不断延伸推广,有些原来很少用的字,现在变成了常用字,例如:朱镕基的“镕”字,未收入GB2312-80,现在大陆的报业出刊只得使用(金+容)、(金容)、(左金右容)等来表示,形式不一而同,这使得表示、存储、输入、处理都非常不方便,对于搜索引擎等软件的构造来说也不是好消息,而且这种表示没有统一标准。
为了解决这些问题,以及配合UNICODE的实施,全国信息技术化技术委员会于1995年12月1日《汉字内码扩展规范》。GB即“国标”,K是“扩展”的汉语拼音第一个字母。 GBK向下与GB2312 完全兼容,向上支持ISO 10646国际标准,在前者向后者过渡过程中起到的承上启下的作用。GBK 亦采用双字节表示,总体编码范围为8140-FEFE之间,首字节在81-FE之间,尾字节在40-FE(不要求大于127)之间,剔除XX7F一条线。

GBK共收入21886个汉字和图形符号,包括:

  • GB2312中的全部汉字、非汉字符号。
  • BIG5中的全部汉字。
  • 与ISO 10646相应的国家标准GB13000中的其它CJK汉字,以上合计20902个汉字。
  • 其它汉字、部首、符号,共计984个。

微软公司自Windows 95 简体中文版开始支持GBK。

GBK编码区分三部分:

  • 汉字区 包括
    GBK/2:OXBOA1-F7FE, 收录GB2312汉字6763个,按原序排列;
    GBK/3:OX8140-AOFE,收录CJK汉字6080个;
    GBK/4:OXAA40-FEAO,收录CJK汉字和增补的汉字8160个。
  • 图形符号区 包括
    GBK/1:OXA1A1-A9FE,除GB2312的符号外,还增补了其它符号
    GBK/5:OXA840-A9AO,扩除非汉字区。
  • 用户自定义区
    即GBK区域中的空白区,用户可以自己定义字符。

CJK:中日韩统一表意文字(CJK Unified Ideographs),目的是要把分别来自中文、日文、韩文、越文中,本质、意义相同、形状一样或稍异的表意文字(主要为汉字,但也有仿汉字如日本国字、韩国独有汉字、越南的喃字)于ISO 10646及Unicode标准内赋予相同编码。CJK 是中文(Chinese)、日文(Japanese)、韩文(Korean)三国文字的缩写。

GB18030

GB18030 是最新的汉字编码字符集国家标准, 向下兼容 GBK 和 GB2312 标准,增加了对少数民族文字(如藏、蒙古、傣、彝、朝鲜、维吾尔文等)的支持。 GB18030 编码是一二四字节变长编码。一字节部分从 0x0~0x7F 与 ASCII 编码兼容。 二字节部分, 首字节从 0x81~0xFE, 尾字节从 0x40~0x7E 以及 0x80~0xFE, 与 GBK标准基本兼容。 四字节部分, 第一字节从 0x81~0xFE, 第二字节从 0x30~0x39, 第三和第四字节的范围和前两个字节分别相同。 四字节部分覆盖了从 0x0080 开始, 除去二字节部分已经覆盖的所有 Unicode 3.1 码位。也就是说, GB18030 编码在码位空间上做到了与 Unicode 标准一一对应,这一点与 UTF-8 编码类似。
GB18030有两个版本:GB18030-2000和GB18030-2005。GB18030-2000是GBK的取代版本,它的主要特点是在GBK基础上增加了CJK统一汉字扩充A的汉字。GB18030-2005的主要特点是在GB18030-2000基础上增加了CJK统一汉字扩充B的汉字。
Windows XP开始原生支持GB18030.
从ASCII、GB2312、 GBK到GB18030,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。

windows-1251

windows-1251是一种8位字符编码,设计包括使用西里尔脚本语言如俄语、保加利亚语、塞尔维亚斯拉夫语和其他语言。它是最广泛用于编码的保加利亚,塞尔维亚语和马其顿语。

Shift JIS

Shift JIS(Shift Japanese Industrial Standards, SJIS)是一个日本电脑系统常用的编码表。它能容纳全角及半角拉丁字母、平假名、片假名、符号及日语汉字。
它被命名为Shift_JIS的原因,是它在放置全角字符时,要避开原本在0xA1-0xDF放置的半角假名字符。使用单字节或者双字节表示字符。

Unicode

Unicode 是基于国际标准化组织(International Organization for Standardization,简称ISO)所制定的 ISO/IEC 10646 ,即通用字符集(Universal Character Set,简称 UCS)的标准来发展,在1991年首次以书本的形式(The Unicode Standard)对外发布。最新版本的Unicode包含了超过120000个字符(超过了2个字节能表示的范围),覆盖了129中现代及古代语言。
Unicode给每个字符提供了一个唯一的数字,表示为U+xxxxxx,成为code point。
的code point是U+5B87。
CJK_Unicode.png
Unicode只是一种抽象的表示形式,就像漂浮在空中一样,最终在使用的时候要有一种表现形式,如我们表示成5B87,那么我们可不可以表示成875B呢?技术上,当然是可以的,这时一个叫做 BOM(Byte Order Mark)的东西诞生了,我们记录在文件的开头,来标记是否需要交换字节顺序。

UTF-16

UTF-16 (16-bit Unicode Transformation Format) 可以编码所以的Unicode,是一种变长编码形式,每个code point被编码成一个或者两个16位的双字节数。
UTF-16中的所有ASCII码,都是多了一个00字节。
UTF-16 是从16位定长的编码UCS-2(定长的2字节UCS字符集)发展未来,由于一个2字节定长的编码不够表示所有的字符,UCS-2已经被废弃。

U+0000~U+D7FF以及U+E000~U+FFFF编码是16位的code point;
U+D800~U+DFFF保留,不使用;
U+10000~U+10FFFF采用算法转换成两个16位编码:
先减去0x010000得到一个20位的数,然后分成高10位和低10位两个数,高10位加0xD800,低10位加0xDC00.
UTF-16BE
Windows API,Java,JavaScript使用UTF-16.

UTF-8

UTF-8可以编码所有的unicode,由Ken Thompson(Unix、C的发明者之一)于1992年创建,采用变长编码形式,可以单字节向下兼容ASCII码,可以避免字节序,所以不必使用BOM。
UTF-8是主流的万维网编码方式,2016年1月统计大约有86%的网页使用UTF-8。
转换表如下:

Bits of code point First code point Last code point Bytes in sequence Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6
7 U+0000 U+007F 1 0xxxxxxx
11 U+0080 U+07FF 2 110xxxxx 10xxxxxx
16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-8的优点是与ASCII码完全兼容,没有字节序问题,但是表示汉字需要3个字节。
UTF-8 w/ BOM

UTF-32

UTF-32采用定长编码形式,unicode的每个code point对应1个32bit的数。简单粗暴,占空间大。
Python使用UTF-32.

BOM(Byte Order Mark)

UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字 节序。例如“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎” 还是“乙”?
Unicode规范中推荐的标记字节顺序的方法是BOM。BOM是一个有点小把戏的做法:
在Unicode中有一个叫做“ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是U+FEFF。
而FFFE在Unicode中是不存在的字符,所以不应该出现在实际传输中。Unicode规范建议我们在传输字节流前,先传输字符”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编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
Windows就是使用BOM来标记文本文件的编码方式的。
UTF编码 ║ Byte Order Mark   
UTF-8  ║ EF BB BF   
UTF-16LE ║ FF FE   
UTF-16BE ║ FE FF   
UTF-32LE ║ FF FE 00 00   
UTF-32BE ║ 00 00 FE FF

MBCS

MBCS(Multi-Byte Chactacter System(Set))多字节字符系统或者字符集,对一个字符的表示实际上无法确定他需要占用几个字节的,只
能从编码本身来区分和解释。因此计算机在存储的时候,就是采用多字节存储的形式。也就是你需要几个字节我给你放
几个字节,比如A我给你放一个字节,比如”中“,我就给你放两个字节,这样的字符表示形式就是MBCS。
在基于GBK的windows中,不会超过2个字节,所以windows这种表示形式有叫做DBCS(Double-Byte Chactacter System),其实算是MBCS的一个特例。

tranform between unicode and character using Python

Python字符的内部编码是原生支持unicode的,python2内部使用utf-8来存储字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
encode decode
unicode----->-----str------->-------unicode
```
```python character -> unicode
sys.getfilesystemencoding() #mbcs #windows charset
type('宇') #str
type(u'宇') #unicode
str('宇') #'\xe5\xae\x87'
str(u'宇') #UnicodeEncodeError: 'ascii' codec can't encode character u'\u5b87' in position 0: ordinal not in range(128)
hex(ord(u'宇')) #'0x5b87'
'宇'.decode('unicode-escape') #u'\xe5\xae\x87' #Python internal use utf-8
u'宇'.encode('unicode-escape') #'\\u5b87'
u'宇'.encode('utf-8') #'\xe5\xae\x87'
u'\u5b87'.encode('utf-8') #'\xe5\xae\x87'
u'宇'.encode('utf-16') #'\xff\xfe\x87[' #0xfffe is BOM '[' is 0x5b,little endian
u'宇'.encode('utf-16-le') #'\x87['
u'宇'.encode('utf-16-be') #'[\x87'
u'宇'.encode('gb2312') #'\xd3\xee'
u'宇'.encode('gbk') #'\xd3\xee'
u'宇'.encode('big5') #'\xa6t' #'t'=0x74
u'宇'.encode('mbcs') #'\xd3\xee' #windows use gb2312 compatible charset

unicode -> character
1
2
3
4
5
6
7
print unichr(0x5b87) #宇
print u'\u5b87' #宇
print b'\xE5\xAE\x87'.decode('utf-8') #宇
print bytearray([0x87, 0x5b]).decode('utf-16-le') #宇
print bytearray([0x5b, 0x87]).decode('utf-16-be') #宇
print bytearray([0xd3, 0xee]).decode('gb2312') #宇

总结

中文网页基本上使用GBK或者UTF-8.
英文网页基本使用UTF-8.
中文windows 10 默认支持GBK。
一些OS和编程语言的API使用UTF-16或者UTF-32.
windows下winhex或者notepad++可以转换文件存储的编码格式。

Reference:

  1. ASCII
  2. ASCII、Unicode、GBK和UTF-8字符编码的区别联系
  3. 汉字字符集编码查询
  4. GB2312
  5. BIG-5
  6. 中文字符编码简介 GB2312/GBK/GB18030/BIG5
  7. GB18030
  8. Windows-1251
  9. Unicode
  10. The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
  11. UTF-8
  12. UTF与BOM之间的关系
  13. Python Unicode Integration
  14. Standard Encodings in Python