本文将会介绍Python2和Python3中的字符编码。

实验环境需要Python2和Python3。install Python2 & Python3


首先我们要理清楚一些概念。

字节与字符

计算机的世界是由用0和1组成,这叫比特bit。当计算机存储一个文件时,文件是由一串01的字节序列构成,字节Byte等于8个bit。字符是一个符号,汉字、日语、数字、标点等。字节"01100001"方便存储和传输、字符a方便阅读。

编码与解码

字符转换为字节的过程叫做编码encode;反过来,将字节转换成字符的过程叫做解码。

encode_decode

这里有个问题,我的字符"a"为什么编码成了0110001,而不是01010101这种动词打次的节奏?这就有了字符集的概念,它为包含的每一个字符指定了一个唯一数字(编码)。在这里a的编码是97写成二进制就是0110001下面介绍两种字符集ASCII和Unicode

ASCII

ASCII(American Standard Code for Information Interchange)最开始只定义了128个字符编码。字符与ASCII码的对应关系可以查看ascii-code

当非英语国家人开始使用电脑,这个字符集所包含的字符就不够用了。于是各个国家有了自己的字符集,中文的字符集就经历了从GB2312到GBK再到GB18030的过程。但是,各个国家都有自己的一套标准,这样子是没有办法交流的,所以就有的Unicode。

Unicode

为了统一地球上所有字母、符号,ISO(国际标准化组织)制定了Universal Multiple-Octet Coded Character Set,简称UCS,俗称Unicode。它给世界范围内每个character指定了一个唯一数字。字符与Unicode码表unicode-chart

Unicode有两种格式:UCS-2和UCS-4,分别表示使用2个字节和4个字节编码。如在python2中的UCS编码是5b57就是两个字节[1]。不过两个字节最多表示65535个字符,而4个字节可以表示到100多万个字符。

有了统一Unicode码后,这里就涉及到了如何把一个Unicode码转换成Bytes

UTF-8

UTF-8 (Unicode Transformation Format)就是最主要的一种将Unicode码U+597D转换成字节\xe5\xa5\xbd的编码方式[3]。它是一套以8位为一个编码单位的可变长编码,会将一个unicode码编码为1到4个字节。

使用UTF-8编码后,原来的ASCII字符集中的字符还是使用一个字节表示,这是对与ASCII很好的兼容性。

Unicode符号范围(十六进制) UTF-8编码方式(二进制)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

以汉字为例unicode码为597D,对应的区间是0000 0800--0000 FFFF从上表可对应到3个字节的UTF-8编码,通过如下示例中转换最终得到16进制utf-8码\xe5\xa5\xbd

    中文        好
    unicode         0101   100101   111101  # U+597D
    编码规则     1110xxxx 10xxxxxx 10xxxxxx
                --------------------------
    utf-8       11100101 10100101 10111101
                --------------------------
    16进制utf-8     e   5    a   5    b   d

Python2

理清楚了上面的概念后,来说说在Python中的unicode、utf-8、编码。

先从Python2中说起,Python2有如下两个特点:

  • Python2中使用ASCII作为默认编码方式。
  • Python2中字符串可以使用unicode和str两种类型表示。

对于这个字,用str表示时,它对应的就是utf-8编码的(Bytes)'\xe5\xa5\xbd',而用unicode码就是u'\u597d'[4]。从Byte到unicode是解码,从unicode到Byte时encode。

In [2]: type('好'), type(u'好'), '好', '好'.decode('utf8'), u'好'.encode('utf8')
Out[2]: (str, unicode, '\xe5\xa5\xbd', u'\u597d', '\xe5\xa5\xbd')

对于unicode和str的长度。每个unicode码都是长度为1,str的长度则是表示str的byte个数。如汉字使用UTF8编码后都占3个字节。

In [3]: len(u'你好'), len(u'你好'.encode('utf8'))
Out[3]: (2, 6)

In [4]: len(u'hi \u2119'), len('hi')
Out[4]: (4, 2)

Python2中使用ASCII作为默认编码。但是ASCII编码的字符范围很小,一不小心遇到中文就很容易出错。

In [5]: sys.getdefaultencoding()
Out[5]: 'ascii'

下面举几个容易出现问题的情况:

  1. 在编码和解码时字符集不匹配,超出了编码范围。

    >>> my_unicode = u'你好'
    >>> my_unicode.encode('ascii')
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
    
  2. str和unicode混着使用时,implicityly decode[5]。实际是使用sys.getdefaultencoding()来decode了str后和unicode做字符串连接。

    >> u'你好' + 'world'
    u'\u4f60\u597dworld'
    >>> u'你好' + '世界'  # '世界'.decode('ascii')
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
    
  3. 在文件中有非ascii字符时,文件开头缺少code encoding信息。

    # -*- coding: utf8 -*- # 如果缺少code encoding信息,python t.py时也会出现编码问题。
    print '你好'
    

Python3

Python3中,我还没有用起来。


参考资料:


注[1]:想知道python使用的unicode是两个字节还是四个字节。

In [28]: import sys; sys.maxunicode  # python3是四个字节
Out[28]: 1114111
In [56]: import sys; sys.maxunicode  # python2是两个字节
Out[56]: 65535

注[2]:python3如何获得unicode码。

注[3]:常见的有UTF8和UTF16,分别表示以8位和16位为单位对unicode进行编码。

注[4]:需要补充一点的是,str类型的字符其具体的编码格式是UTF-8还是GBK,还是其他格式,根据操作系统相关。这点我也需要再理清楚一下。

注[5]:str和unicode混着使用,除了+外,其他很多情况也都会implicityly decode和encode。

"Title %s" % my_unicode # 'Title' -> decode
u'Title %s' % my_utf8  # my_utf8 -> decode
print my_unicode       # 我的环境竟然没有出错,不是应该ascii encode错了。