Python编码 - 无法解码为utf8

6
我有一个由外部程序填充的sqlite数据库,我试图用Python读取其中的数据。但是,当我尝试读取数据时,会出现以下错误:OperationalError: Could not decode to UTF-8 如果我在sqlite管理器中打开数据库,并使用内置的浏览和搜索查看有问题的记录中的数据,看起来一切正常。但是,如果我将表导出为CSV文件,我会注意到有问题的记录中的字符£变成了£ 如果我在Python中读取CSV文件,有问题的记录中的£仍然被读取为£,但这不是问题,我可以手动解析它。但是,我需要直接从数据库中读取数据,而不需要将其转换为csv的中间步骤。
我查看了一些在线答案以获取类似的问题的解决方法,到目前为止,我已经尝试了设置"text_factory = str"并尝试使用sqlite管理器将列的数据类型从TEXT更改为BLOB,但仍然出现错误。
下面的代码导致了OperationalError:Could not decode to UTF-8错误。
conn = sqlite3.connect('test.db')
conn.text_factory = str
curr = conn.cursor()
curr.execute('''SELECT xml_dump FROM hands_1 LIMIT  5000  , 5001''')
row = curr.fetchone()

数据库中5000条以上的记录出现了字符问题,因此导致出现错误。

非常感谢您的帮助。


你尝试将其解码为UTF-16了吗? - Natecat
等等,你为什么在那里使用文档字符串? - Natecat
如果我尝试这样做,会出现错误:NameError:全局名称'unicode'未定义。 - Java Fish
你有那个外部程序的源代码吗? - CL.
@Natecat 这不也是多行字符串字面量的写法吗?尽管如此,他的查询也不是多行的...通常我使用双引号,因为我很野蛮。 - foxtrotuniform6969
显示剩余3条评论
2个回答

25

Python试图通过将一些以字节形式存储在数据库中的文本转换为Python str对象来提供帮助。为了进行这种转换,Python必须猜测每个查询返回的字节(或字节组)表示什么字母。默认的猜测是一种称为utf-8的编码。显然,在您的情况下,这种猜测是错误的。

解决方法是给Python一点提示,告诉它如何将字节映射到字符(即Unicode字符)。您已经接近答案:

conn.text_factory = str

然而(基于您在上面的评论中的回复),由于您正在使用Python 3,str 是默认的文本工厂,因此该行不会对您产生新的影响(请参见文档)。

这行代码背后发生的事情是Python尝试使用str函数将查询返回的字节转换为字符串,类似于:

your_string = str(the_bytes, 'utf-8') # actually uses `conn.text_factory`, not `str`

...但你希望使用另一种编码,其中'utf-8'是首选。由于您无法更改str函数的默认编码,因此您必须以其他方式模拟它。您可以使用一次性的无名称函数称为lambda来实现:

conn.text_factory = lambda x: str(x, 'latin1')

现在当数据库将字节交给Python处理时,Python会尝试使用'latin1'编码方案而不是'utf-8'编码方案将它们映射为字母。当然,我不知道latin1是否是您数据的正确编码方式。实际上,您需要尝试一些编码方式才能找到正确的编码方式。我建议首先尝试以下方式:

  • 'iso-8859-1'
  • 'utf-16'
  • 'utf-32'
  • 'latin1'

您可以在此处找到更完整的列表(链接)

另一个选择是让从数据库出来的字节保持为字节。这对您是否是个好主意取决于您的应用程序。您可以通过设置以下代码来实现:

conn.text_factory = bytes

1
SQLite始终使用UTF-8编码。如果一个字符串实际上不是UTF-8编码,那么在第一次插入数据库时就会出现错误。 - CL.
@CL。没错,但从文档来看:SQLite对接收的文本并不挑剔,并且非常乐意处理未规范化或甚至格式不正确的UTF-8或UTF-16文本字符串。因此,想要存储IS08859数据的程序员可以使用UTF-8接口来实现。只要不尝试使用UTF-16排序序列或SQL函数,文本的字节序列就不会被修改。 - Apis Utilis
谢谢您。我收到了一个使用Python2生成的SQLite数据库,其中原始内容被编码为Latin1。在Python2中,conn.text_factory = str这行代码似乎可以用于读取此数据库,但是我正在使用Python3,而这种方法无法奏效。您提供的lambda函数非常有效,让我的程序能够正常工作。 - SuperTetelman
我正在使用 pandasqlpandas 数据框上编写查询语句 out=ps.sqldf(query, locals())。我没有连接到任何数据库。那么该如何解决呢? - MAC

6

如果数据库中的文本实际上大多数是使用UTF-8编码的,但您仍然看到此错误(无法解码为UTF-8),那么问题可能是一个或多个行具有虚假数据,这些数据不是有效的UTF-8。默认情况下,Python的decode()函数会在看到这样的文本时引发异常。如果您处于这种情况并希望简单地忽略这些错误,则可以设置一个text_factory,如下所示:

conn = sqlite3.connect('my-database.db')
conn.text_factory = lambda b: b.decode(errors = 'ignore')

我正在使用 pandasqlpandas 数据框上编写查询语句 out=ps.sqldf(query, locals())。我没有连接到任何数据库。那么该如何解决呢? - MAC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接