Python2编码问题

使用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\xd2

>>> s = '我'
>>> s
'\xce\xd2'
>>> type(s)
<type 'str'>

而unicode类型的 ‘我’ 对应的unicode符号为 u’\u6211’

>>> u=u'我'
>>> u
u'\u6211'
>>> type(u)
<type 'unicode'>

而unicode符号保存到文件或者传输到网络需要经过 encode 编码转化成str类型。反之,从str类型经过 decode 转化成 unicode。

encode

>>> u = u'我'
>>> u
u'\u6211'
>>> u.encode('utf-8')
'\xe6\x88\x91'

decode

def de():
    s = '我'
    print(s)
    b = s.decode('utf-8')
    print(b)

小结:str本质是一串二进制数据,而 unicode 是字符(符号),编码(encode)就是把字符(符号)转换为 二进制数据的过程,因此 unicode 到 str 的转换要用 encode 方法,反过来就是用 decode 方法。

UnicodeEncodeError

UnicodeEncodeError 发生在unicode字符串转换成str字节序列的时候,例如把unicode字符串保存到文件

# -*- 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 码来编码。

相当于:

>>> u"测试".encode("ascii")

但是,我们知道 ASCII 字符集中只包含了128个拉丁字母,不包括中文字符,因此 出现了 ‘ascii’ codec can’t encode characters 的错误。要正确地使用 encode ,就必须指定一个包含了中文字符的字符集,比如:UTF-8、GBK。

def main():
    name = u'测试'
    name = name.encode('utf-8')
    with open('out.txt','w') as f:
        f.write(name)

UnicodeDecodeError

UnicodeDecodeError 发生在 str 类型的字节序列解码成 unicode 类型的字符串时

>>> a = u'我'
>>> a
u'\u6211'
>>> b = a.encode('utf-8')
>>> b
'\xe6\x88\x91'
>>> b.decode('gbk')
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的又一个例子

>>> x = u'Python'
>>> 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字符集中不包含有中文,所以报错了。
相当于执行了:

y.decode('ascii')

正确地方式应该是找到一种包含有中文字符的字符编码,比如 UTF-8或者 GBK 显示地把 y 进行解码转换成 unicode 类型.

def decodeTest():
    x = u'Python'
    y = '大神'
    y = y.decode('utf-8')
    print(x+y)

详情参见,原文出处:http://python.jobbole.com/88264/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值