一、解码问题: ‘utf-8’ codec can’t decode byte 0xa8 in position xx: invalid start byte
【问题描述】:
笔者通过Python3从数据库(HBase)中取数据的时候,报错如下:
报错信息显示,在内置函数转换获取的数据(字节数组)为字符串str时,UTF-8编码方案无法对字节\xa8
进行解码,该字节0xa8
在当前字节数组的索引是21
(从0开始计数)。
【问题分析】:
在解决这个问题之前,我们首先来回顾一下Python3中编码和解码之间的转换关系如下(欲全面了解编码和解码问题详情点击此处):
可见,当数据含有中文或其他特殊字符时,Unicode码被编码成非UTF-8和非ASCII码(如GBK码)后,再用UTF-8解码就会出错。因为,它们根本就不是一套编码方案。
本例中,我无法解码的数据在本地的显示是:
Ecole Polytechnique Fédérale de Lausanne (EPFL)
在数据库中的显示是:
我们都知道Python中,UTF-8
码会将Unicode
码中的每个非ASCII字符编码成2~3个字节(中文是3个字节,这里的é
是两个字节),格式是\x##
。
所以,我们可以推断出,在数据被传输或存储前,Unicode
码所表示的str,被转换成了非UTF-8
编码。这自然在编辑或获取数据时,无法用UTF-8
码解码成Unicode
码,上例中的第一个非ASCII字节\xA8
即为报错信息索引为21
中的0xa8
。
至此,我们完全理解了为什么会出错,和具体在哪个位置出的错。
【解决思路】:
- 在str数据被传输或存储前,保证其为
Unicode
码,再对其进行UTF-8编码(.encode('utf-8')
)。 - 在str数据被编辑或获取前,对其进行UTF-8解码(
.decode('utf-8')
)为Unicode
码。 - 对于需要和数据库交互的情况,往往譬如
insert()
或get()
等方法都会内置字符串str和字节数组bytes之间的转换函数,为防止其不被转换成GBK等其他编码,所以只要在传入数据前保证其为Unicode
码即可。
本例中我的解决方案:
保证上传的字段value
为Unicode
码,即成功。
def function(...,value=str(value).encode('utf-8').decode('utf-8'),...)
被utf-8
编码后的数据在数据库中的显示,该字节可被utf-8
解码为Unicode
码:
二、编码问题:‘gbk’ codec can’t encode character ‘\uxxxx’ in position xxxxx: illegal multibyte sequence
【问题描述】:笔者在Windows系统下尝试将数据库中数据读取出来,并写入到本地.txt文件中报错。
报错代码如下:
with open('C:\\Users\\Administrator\\xxx\\content.txt', 'w') as f:
f.write(str_data)
读取亦然:
with open('C:\\Users\\Administrator\\xxx\\content.txt', 'r') as f:
str_data = f.read()
意思是在索引为26088的'\u0141'
Unicode码表示的字节无法被GBK
编码方案编码。
【问题分析】:
Windows系统默认是GBK编码,所以在单独设置编码方案的时候,系统会自动默认为GBK编码,对于含有非中文的字符就无法编码。
【解决方案】:
不论数据中是否含有中文等非ASCII字符,直接编码成UTF-8
即可。
with open('C:\\Users\\Administrator\\xxx\\content.txt', 'w', encoding='utf-8') as f:
f.write(str_data)