【问题标题】:UnicodeDecodeError Loading with sqlalchemyUnicodeDecodeError 使用 sqlalchemy 加载
【发布时间】:2016-08-09 16:06:11
【问题描述】:

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

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

表中的一列被定义为Unicode(500),所以这个错误提示我有一个条目被截断,因为它超过 500 个字符。有没有办法处理这个错误并仍然加载条目?有没有办法找到错误的条目并将其删除,而不是尝试逐个(或分批)加载每个条目直到我收到错误?

【问题讨论】:

  • 听起来好像执行截断的任何东西都没有意识到字符集,这是最初的错误。您可以尝试让 MySQL 将该列转换为二进制,然后再转换回 UTF8 — 我认为应该强制将不完整的字符替换为 ?
  • 你可以从包含完整的回溯开始;这样我们至少可以诊断解码是否可以在其他地方完成,或者可以配置为采用错误处理程序。

标签: python mysql unicode utf-8 sqlalchemy


【解决方案1】:

简而言之,你应该改变:

Unicode(500)

到:

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

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

发生了什么是当你解码一个字节串时,它会抱怨如果字节串不能被解码,你会看到错误。

>>> 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 参数传递给构造函数,该构造函数将其值传递给编码器的错误参数。还有一个注意事项,您需要设置convert_unicode='force' 才能使其工作。

因此,Unicode(500, unicode_errors='ignore', convert_unicode='force') 应该可以解决您的问题,前提是您可以截断数据的末端。

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

【讨论】:

    【解决方案2】:

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


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

    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 的禅宗没有什么不同:

    显式优于隐式。

    错误绝不应该悄无声息地过去。
    除非明确静音。
    面对模棱两可,拒绝猜测的诱惑。

    【讨论】:

      【解决方案3】:

      将您要存储的列设为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_.
      

      在您尝试了一些类似的事情之后,我们可以看到下一步该采取什么方向。

      【讨论】:

        猜你喜欢
        • 2012-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-13
        • 1970-01-01
        • 2018-11-07
        • 1970-01-01
        • 2017-04-19
        相关资源
        最近更新 更多