Python编码中出现的问题

如果你正在使用 Python2.x,那么你一定遇到了一些或者很多关于字符编码解码报错情况。是的,所以,如果你愿意,让我们一层一层一层的剥开它的心,你会发现你会讶异,真TM的简单。

相关概念

  • 计算机中的一切均为bytes(字节)。硬盘中的文件为一系列的byte组成,网络中传输的只有byte。所有的信息,在你写的程序中进进出出的,均由byte组成
  • 字符:我们现在看到的英文字母、中文汉字就是经过计算机解码后对人类友好的抽象符号的表现
  • encode() 编码: 将字符转换成二进制流
  • decode() 解码: 将二进制流转换成字符

coding=utf-8和sys.getdefaultencoding()

如果我们的 py文件中包含中文,往往需要在第一行或者第二行添加:
# coding=utf-8 或者 # -*- coding: utf-8 -*-

这表示声明该 py文件中定义的字符串变量使用的编码方式为 utf-8。
python2.x 中,

1
2
>>> sys.getdefaultencoding()
'ascii'

python3.x 中,

1
2
>>> sys.getdefaultencoding()
'utf-8'

注意:

在使用 encode() 和 decode() 的时候,如果不传入任何参数,那么 python 解释器就会使用 sys.getdefaultencoding() 所指代的编码解码方式进行 encode() 和 decode()


Python2.x的字符编码问题

1.在python2.x中,str 等价于bytes,是由unicode经过编码(encode)后的字节组成的字节串。在 python2.x 中你不加任何修饰直接定义的字符串,其实是字节串,切记这一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> str == bytes
True

>>> '我们' == b'我们'
True

>>> b'我们' == str('我们')
True

>>> b'我们'
'\xce\xd2\xc3\xc7'

>>> len('我们')
4
#len('我们')在windows平台下长度为4(默认编码为GBK),Linux平台下长度为6(默认编码为UTF-8)

2.unicode 才是真正意义上的字符串,是由 str 解码(decode)后的字符组成的字符串。定义unicode 字符串,直接在 string 前面加前缀 u 即可

1
2
3
4
5
6
7
8
9
10
>>> len(u'我们')
2

>>> unicode('我们', 'utf-8')
u'\u6211\u4eec'

>>> '我们'.decode('utf-8')
u'\u6211\u4eec'

#无论是在Windows下还是在Linux下 u'我们' 的字符串长度都为2,这正是我们所想要的结果。

3.相互转换
str –(decode)–> unicode –(encode)–> str


Python3.x的字符编码问题

在 python3.x 中,字符编码问题就变得不那么混乱了,具体看下面:

1.bytes 为字节串,如果你想定义一个字节串,不像在 python2.x 中那样直接定义就行了。在 python3.x 中,一般使用下面两种方式定义一个字节串:

1
2
3
4
5
#第一种方法,加上 b 前缀
>>> b'I am a byte'

#第二种方法:
>>> bytes(something, encodeing='xxx')

如果想要定义一个含有中文的字节串,必须使用第二种方法,并且将 encoding参数设置为你为该 py文件设置的字符编码方式,即你在 py文件的头部设置的类似于 # coding: utf-8 中的编码方式。

2.str,采用 unicode 方式编码的字符串。无论是 ‘我们’ 还是 u’我们’,都是str对象

1
2
3
4
5
6
7
8
>>> isinstance('我们',str)
True

>>> isinstance(u'我们',str)
True

>>> len('我们')
2

3.相互转换
bytes –(decode)–> str –(encode)–> bytes

总结建议

1.在 python2.x 中,对两个字符串进行操作时,如果这两个字符串有一个是 unicode 编码,有一个是非 unicode 编码,python 会自动使用 sys.getdefaultencoding() 的解码方式将非 unicode 编码的字符串 decode 成 unicode 编码,再进行字符串操作,python2.x 悄悄掩盖掉了 byte 到 unicode 的转换,很容易出现问题。而在 python3.x 中,取消了 bytes 和 unicode 之间的自动隐性转换。

2.在需要转换的时候,全部显式转换。从字节解码成文本,用 your_string.decode(encoding),从文本编码成字节,用 your_string.encode(encoding)。

3.任何可能包含中文的字符串,请全部加上 u 前缀,这能减少很多问题。

4.从外部(网页、文件、数据库等)读取数据时,读取的是字节串,应该将其 decode 成 unicode进行使用;当需要向外部输出字符串时,用该外部媒介所能接收的编码形式 encode 字符串后再传递给它。