Maine

纵有疾风起,人生不言弃

Python 中的字符编码

最近遇到了这样一个报错: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),这是很典型的 UnicodeEncodeErrorUnicodeDecodeError 错误,于是回顾了一下Python中的字符编码,做一下记录。

Python 中的默认编码

众所周知,Python分为 Python2.x版本和3.x版本,不同版本的Python默认编码并不一样。pytho2.x 中默认编码是 ‘ascii’,python3.x 中默认是 ‘utf-8’,验证如下:

  • python2.x
Python 2.7.10 (default, Oct  6 2017, 22:29:07) 
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
  • python3.x
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 03:13:28) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getdefaultencoding()
'utf-8'

Python2 中的字符类型

在py2中,有两种字符串类型:str类型和unicode类型;str 类型的字符串有多种编码方式,默认是 ascii,还有 gbk,utf-8 等,unicode 类型的字符串使用 u’…’ 的形式来表示。

两种字符间转换

先来看一下两种字符串对解释器而言是什么样的状态

# coding=utf-8
str = "菜鸟"
ustr = u"菜鸟"
print(type(str))  # <type 'str'>
print(repr(ustr)) # '\xe8\x8f\x9c\xe9\xb8\x9f'
print(type(ustr)) # <type 'unicode'>
print(repr(ustr)) # u'\u83dc\u9e1f'

可以看到,str类型存储字节数据,Unicode类型存储unicode数据,那么,两种类型怎么互相转换呢?

# coding=utf-8
str = "菜鸟"
ustr = u"菜鸟"

d_str = str.decode("utf-8")
e_ustr = ustr.encode("utf-8")
print(type(d_str)) # <type 'unicode'>
print(repr(d_str)) # u'\u83dc\u9e1f'
print(type(e_ustr)) # <type 'str'>
print(repr(e_ustr)) # '\xe8\x8f\x9c\xe9\xb8\x9f'

无论是 utf8,gbk还是ascii都只是一种编码规则,用于把unicode数据编码成字节数据,所以utf8编码的字节一定要用utf8的规则解码,否则就会出现乱码或者报错的情况。这也与 UnicodeEncodeError & UnicodeDecodeError 有很大关系,下面再看一下:

UnicodeEncodeError & UnicodeDecodeError 根源:

如果代码里面同时使用了 str 类型和 unicode 类型的字符串,Python2 会默认使用 ascii 编码尝试对 unicode 类型的字符串进行编码,或对 str 类型的字符串解码操作,如果你的字符串当中又恰好有非 ascii 字符,比如中文,那么一个BUG就诞生了。

看个例子:

>>> s = '我是菜鸟' # str 类型, utf-8 编码
>>> u = u'我想飞'  # unicode 类型
>>> s + u         # 会进行隐式转换,即 s.decode('ascii') + u
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)

为了避免出错,我们就需要显示指定使用 ‘utf-8’ 进行解码,如下:

 >>> s = '我是菜鸟' # str 类型, utf-8 编码
 >>> u = u'我想飞'  # unicode 类型
 >>> s + u         # 会进行隐式转换,即 s.decode('ascii') + u
 >>> print(a + b.encode("utf-8"))
 我是菜鸟我想飞

还有一种情况,如果函数或类等对象接收的是 str 类型的字符串,但你传的是 unicode,Python2 会默认使用 ascii 将其编码成 str 类型再运算,这时就很容易出现 UnicodeEncodeError。

>>> u_str = u'菜鸟'
>>> str(u_str) # str(u_str.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)

报错场景还有很多,原理就是这样,遇见错误需要自己举一反三。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注