字符编码是计算机编程中不可回避的问题,不管你用 Python2 还是 Python3,亦或是 C++, Java 等,我都觉得非常有必要厘清计算机中的字符编码概念。本文主要分以下几个部分介绍:
- 基本概念
- 常见字符编码简介
- Python 的默认编码
- Python2 中的字符类型
- UnicodeEncodeError & UnicodeDecodeError 根源
基本概念
字符(Character)
在电脑和电信领域中,字符是一个信息单位,它是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。比如,一个汉字,一个英文字母,一个标点符号等都是一个字符。字符集(Character set)
字符集是字符的集合。字符集的种类较多,每个字符集包含的字符个数也不同。比如,常见的字符集有 ASCII 字符集、GB2312 字符集、Unicode 字符集等,其中,ASCII 字符集共有 128 个字符,包含可显示字符(比如英文大小写字符、阿拉伯数字)和控制字符(比如空格键、回车键);GB2312 字符集是中国国家标准的简体中文字符集,包含简化汉字、一般符号、数字等;Unicode 字符集则包含了世界各国语言中使用到的所有字符字符编码(Character encoding)
字符编码,是指对于字符集中的字符,将其编码为特定的二进制数,以便计算机处理。常见的字符编码有 ASCII 编码,UTF-8 编码,GBK 编码等。一般而言,字符集和字符编码往往被认为是同义的概念,比如,对于字符集 ASCII,它除了有「字符的集合」这层含义外,同时也包含了「编码」的含义,也就是说,ASCII 既表示了字符集也表示了对应的字符编码。
常见字符编码简介
常见的字符编码有 ASCII 编码,GBK 编码,Unicode 编码和 UTF-8 编码等等。这里,我们主要介绍 ASCII、Unicode 和 UTF-8。
ASCII
计算机是在美国诞生的,人家用的是英语,而在英语的世界里,不过就是英文字母,数字和一些普通符号的组合而已。
在 20 世纪 60 年代,美国制定了一套字符编码方案,规定了英文字母,数字和一些普通符号跟二进制的转换关系,被称为 ASCII (American Standard Code for Information Interchange,美国信息互换标准编码) 码。
比如,大写英文字母 A 的二进制表示是 01000001(十进制 65),小写英文字母 a 的二进制表示是 01100001 (十进制 97),空格 SPACE 的二进制表示是 00100000(十进制 32)。Unicode
ASCII 码只规定了 128 个字符的编码,这在美国是够用的。可是,计算机后来传到了欧洲,亚洲,乃至世界各地,而世界各国的语言几乎是完全不一样的,用 ASCII 码来表示其他语言是远远不够的,所以,不同的国家和地区又制定了自己的编码方案,比如中国大陆的 GB2312 编码 和 GBK 编码等,日本的 Shift_JIS 编码等等。
虽然各个国家和地区可以制定自己的编码方案,但不同国家和地区的计算机在数据传输的过程中就会出现各种各样的乱码(mojibake),这无疑是个灾难。
怎么办?想法也很简单,就是将全世界所有的语言统一成一套编码方案,这套编码方案就叫 Unicode,它为每种语言的每个字符设定了独一无二的二进制编码,这样就可以跨语言,跨平台进行文本处理了,是不是很棒!
Unicode 1.0 版诞生于 1991 年 10 月,至今它仍在不断增修,每个新版本都会加入更多新的字符,目前最新的版本为 2016 年 6 月 21 日公布的 9.0.0。
Unicode 标准使用十六进制数字,而且在数字前面加上前缀 U+
,比如,大写字母「A」的 unicode 编码为 U+0041
,汉字「严」的 unicode 编码为 U+4E25
。更多的符号对应表,可以查询 unicode.org,或者专门的汉字对应表。UTF-8
Unicode 看起来已经很完美了,实现了大一统。但是,Unicode 却存在一个很大的问题:资源浪费。
为什么这么说呢?原来,Unicode 为了能表示世界各国所有文字,一开始用两个字节,后来发现两个字节不够用,又用了四个字节。比如,汉字「严」的 unicode 编码是十六进制数 4E25
,转换成二进制有十五位,即 100111000100101,因此至少需要两个字节才能表示这个汉字,但是对于其他的字符,就可能需要三个或四个字节,甚至更多。
这时,问题就来了,如果以前的 ASCII 字符集也用这种方式来表示,那岂不是很浪费存储空间。比如,大写字母「A」的二进制编码为 01000001,它只需要一个字节就够了,如果 unicode 统一使用三个字节或四个字节来表示字符,那「A」的二进制编码的前面几个字节就都是 0,这是很浪费存储空间的。
为了解决这个问题,在 Unicode 的基础上,人们实现了 UTF-16, UTF-32 和 UTF-8。下面只说一下 UTF-8。
UTF-8 (8-bit Unicode Transformation Format) 是一种针对 Unicode 的可变长度字符编码,它使用一到四个字节来表示字符,例如,ASCII 字符继续使用一个字节编码,阿拉伯文、希腊文等使用两个字节编码,常用汉字使用三个字节编码,等等。
因此,我们说,UTF-8 是 Unicode 的实现方式之一,其他实现方式还包括 UTF-16(字符用两个或四个字节表示)和 UTF-32(字符用四个字节表示)。
Python 的默认编码
Python2 的默认编码是 ascii,Python3 的默认编码是 utf-8,可以通过下面的方式获取:
Python2 中的字符类型
Python2 中有两种和字符串相关的类型:str 和 unicode,它们的父类是 basestring。其中,str 类型的字符串有多种编码方式,默认是 ascii,还有 gbk,utf-8 等,unicode 类型的字符串使用 u'...' 的形式来表示,下面的图展示了 str 和 unicode 之间的关系:
UnicodeEncodeError & UnicodeDecodeError 根源
Ref:http://funhacks.net/explore-python/Basic/character_encoding.html