使用Python2.x开发最令人头疼的莫过于编码问题了,最常见的错误就是 UnicodeEncodeError、UnicodeDecodeError。
接下来我们就来研究下相关内容
字节与字符
计算机存储数据,比如视频、文本等都是由字节构成的,一个字节等于8个比特位。
字符就是一个符号,如一个汉字、一个字母等。
字符方便阅读,而字节可用于计算机存储和网络传输。硬盘里的东西都是以二进制数据形式存在的,即字节。
编码与解码
当我们在txt文本中写上一个个字符时,它们最终是以二进制字节序列的形式保存在磁盘上的,这种从字符到字节的转换过程就叫做编码(encode),反过来则是解码(decode)。
那么在Python2.x中的编码问题为何如此头疼?
那是因为Python2是默认使用ASCII 字符编码的,而ASCII 编码又不能处理中文。此外在Python诞生时还没有utf-8编码。
str与unicode
Python字符串分为两种类型 unicode 和 str ,本质上 str 是一串二进制字节序列。
下面的例子将str类型的’我’ 打印出16进制的\xce\xd21
2
3
4
5'我' > s =
> s
'\xce\xd2'
> type(s)
<type 'str'>
而unicode类型的 ‘我’ 对应的unicode符号为 u’\u6211’1
2
3
4
5u'我' u=
u
u'\u6211'
type(u)
<type 'unicode'>
而unicode符号保存到文件或者传输到网络需要经过 encode 编码转化成str类型。反之,从str类型经过 decode 转化成 unicode。
encode1
2
3
4
5u'我' u =
u
u'\u6211'
'utf-8') u.encode(
'\xe6\x88\x91'
decode1
2
3
4
5def de():
s = '我'
print(s)
b = s.decode('utf-8')
print(b)
小结:str本质是一串二进制数据,而 unicode 是字符(符号),编码(encode)就是把字符(符号)转换为 二进制数据的过程,因此 unicode 到 str 的转换要用 encode 方法,反过来就是用 decode 方法。
UnicodeEncodeError
UnicodeEncodeError 发生在unicode字符串转换成str字节序列的时候,例如把unicode字符串保存到文件1
2
3
4
5# -*- coding:utf-8 -*-
def main():
name = u'测试'
with open('out.txt','w') as f:
f.write(name)
报错如下:
UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 0-1: ordinal not in range(128)
为什么出现这个错误?
因为调用 write 方法时,Python 会先判断字符串是什么类型,如果是 str,就直接写入文件,
如果字符串是 unicode 类型,那么它会先调用 encode 方法把 unicode 字符串转换成二进制形式的 str 类型,才保存到文件,而 encode 方法会使用 python 默认的 ascii 码来编码。
相当于:1
u"测试".encode("ascii")
但是,我们知道 ASCII 字符集中只包含了128个拉丁字母,不包括中文字符,因此 出现了 ‘ascii’ codec can’t encode characters 的错误。要正确地使用 encode ,就必须指定一个包含了中文字符的字符集,比如:UTF-8、GBK。1
2
3
4
5def main():
name = u'测试'
name = name.encode('utf-8')
with open('out.txt','w') as f:
f.write(name)
UnicodeDecodeError
UnicodeDecodeError 发生在 str 类型的字节序列解码成 unicode 类型的字符串时1
2
3
4
5
6
7
8
9
10u'我' a =
a
u'\u6211'
'utf-8') b = a.encode(
b
'\xe6\x88\x91'
'gbk') b.decode(
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'gbk' codec can't decode byte 0x91 in position 2: incomplete multibyte sequence
- 把一个经过 UTF-8 编码后生成的字节序列 ‘\xe6\x88\x91’再用 GBK 解码转换成 unicode 字符串时,出现 UnicodeDecodeError。
- 因为 (对于中文字符)GBK 编码只占用两个字节,而 UTF-8 占用3个字节,用 GBK 转换时,还多出一个字节,因此它没法解析。避免 UnicodeDecodeError 的关键是保持 编码和解码时用的编码类型一致。
字符 “我”,保存到文件中有可能占3个字节,有可能占2个字节,具体处决于 encode 的时候指定的编码格式是什么。
UnicodeDecodeError的又一个例子1
2
3
4
5
6u'Python' x =
'大神' y =
x + y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xb4 in position 0: ordinal not in range(128)
str 与 unicode 字符串 执行 + 操作时,Python 会把 str 类型的字节序列隐式地转换成(解码)成 和 x 一样的 unicode 类型,但Python是使用默认的 ascii 编码来转换的,而 ASCII字符集中不包含有中文,所以报错了。
相当于执行了:1
y.decode('ascii')
正确地方式应该是找到一种包含有中文字符的字符编码,比如 UTF-8或者 GBK 显示地把 y 进行解码转换成 unicode 类型.1
2
3
4
5def decodeTest():
x = u'Python'
y = '大神'
y = y.decode('utf-8')
print(x+y)
详情参见,原文出处:http://python.jobbole.com/88264/