UnicodeDecodeError使用sqlalchemy加载

时间:2016-08-09 16:06:12

标签: python mysql unicode utf-8 sqlalchemy

我使用sqlalchemy查询MySQL数据库并收到以下错误:

UnicodeDecodeError: 'utf8' codec can't decode bytes in position 498-499: unexpected end of data

表中的一列定义为Unicode(500),因此此错误向我建议有一个被截断的条目,因为它超过500个字符。有没有办法处理此错误仍然加载条目?有没有办法找到错误的条目并删除它除了尝试逐个(或批量)加载每个条目,直到我收到错误?

3 个答案:

答案 0 :(得分:3)

简而言之,你应该改变:

Unicode(500)

为:

Unicode(500, unicode_errors='ignore', convert_unicode='force')

(Python 2代码如下,但原则在python 3中保存;只有部分输出会有所不同。)

当你解码一个bytestring时,它会抱怨如果无法解码bytestring,你会看到错误。

>>> u = u'ABCDEFGH\N{TRADE MARK SIGN}'
>>> u
u'ABCDEFGH\u2122'
>>> print(u)
ABCDEFGH™
>>> s = u.encode('utf-8')
>>> s
'ABCDEFGH\xe2\x84\xa2'
>>> truncated = s[:-1]
>>> truncated
'ABCDEFGH\xe2\x84'        
>>> truncated.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/cliffdyer/.virtualenvs/edx-platform/lib/python2.7/encodings/utf_8.py", 
line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 8-9: unexpected 
end of data

Python提供了处理解码错误的不同可选模式。提出异常是默认设置,但您也可以截断文本或将字符串的格式错误的部分转换为官方的unicode替换字符。

>>> trunc.decode('utf-8', errors='replace')
u'ABCDEFGH\ufffd'
>>> trunc.decode('utf-8', errors='ignore')
u'ABCDEFGH'

这正是列处理中发生的事情。

查看sqlalchemy/sql/sqltypes.py中的Unicode和String类,看起来有一个unicode_errors参数可以传递给构造函数,构造函数将其值传递给编码器的errors参数。还需要注意,您需要设置convert_unicode='force'才能使其正常运行。

因此Unicode(500, unicode_errors='ignore', convert_unicode='force')可以解决您的问题,如果您可以截断数据的末尾。

如果您对数据库有一定的控制权,那么通过定义数据库以使用utf8mb4字符集,您应该能够在将来防止此问题。 (不要只使用utf8,否则它将在四个字节的utf8字符上失败,包括大多数表情符号)。然后,您将保证在您的数据库中存储和返回有效的utf-8。

答案 1 :(得分:0)

将要存储的列设为BLOB。加载数据后,请执行各种操作,例如

 SELECT MAX(LENGTH(col)) FROM ... -- to see what the longest is in _bytes_.

将数据复制到另一个BLOB列并执行

 ALTER TABLE t MODIFY col2 TEXT CHARACTER SET utf8 ... -- to see if it converts correctly

如果成功,那么

 SELECT MAX(CHAR_LENGTH(col2)) ... -- to see if the longest is more than 500 _characters_.

在你尝试了类似的东西后,我们可以看到接下来要采取的方向。

答案 2 :(得分:0)

简而言之,您的MySQL设置不正确,因为它会在中间序列中截断UTF-8字符。我会检查两次MySQL实际上期望在会话和表本身中使用UTF-8的字符编码。

我建议切换到PostgreSQL(认真)以避免这种问题:PostgreSQL不仅在默认配置中正确理解UTF-8,而且它也不会截断字符串以适应值,选择提高而是一个错误:

psql (9.5.3, server 9.5.3)
Type "help" for help.

testdb=> create table foo(bar varchar(4));
CREATE TABLE
testdb=> insert into foo values ('aaaaa');
ERROR:  value too long for type character varying(4)

这也与Python的Zen不同:

  

明确比隐含更好。

  

错误不应该默默地传递。
  除非明确沉默。
  面对模棱两可,拒绝猜测的诱惑。