【问题标题】:UnicodeDecodeError: 'ascii' codec can't decode byte 0x92 in position 47: ordinal not in range(128)UnicodeDecodeError:“ascii”编解码器无法解码位置 47 的字节 0x92:序数不在范围内(128)
【发布时间】:2014-12-24 12:49:53
【问题描述】:

我正在尝试使用 Python 在 StringIO 对象中写入数据,然后最终使用 psycopg2 的 copy_from() 函数将此数据加载到 postgres 数据库中。

首先,当我这样做时,copy_from() 抛出了一个错误:错误:用于编码“UTF8”的无效字节序列:0xc92 所以我关注了this question

我发现我的 Postgres 数据库有 UTF8 编码。

我将数据写入的文件/StringIO 对象显示其编码如下: setgid 非 ISO 扩展 ASCII 英文文本,行很长,带有 CRLF 行终止符

我尝试将写入中间文件/StringIO 对象的每个字符串编码为 UTF8 格式。为此,每个字符串都使用了 .encode(encoding='UTF-8',errors='strict'))。

这是我现在得到的错误: UnicodeDecodeError:“ascii”编解码器无法解码位置 47 中的字节 0x92:序数不在范围内(128)

这是什么意思?我该如何解决?

编辑: 我正在使用 Python 2.7 我的一些代码:

我从 MySQL 数据库中读取数据,该数据库具有按照 MySQL Workbench 以 UTF-8 编码的数据。 这是用于将我的数据(从 MySQL db 获得)写入 StringIO 对象的几行代码:

# Populate the table_data variable with rows delimited by \n and columns delimited by \t
row_num=0
for row in cursor.fetchall() :

    # Separate rows in a table by new line delimiter
    if(row_num!=0):
        table_data.write("\n")

    col_num=0
    for cell in row:    
        # Separate cells in a row by tab delimiter
        if(col_num!=0):
            table_data.write("\t") 

        table_data.write(cell.encode(encoding='UTF-8',errors='strict'))
        col_num = col_num+1

    row_num = row_num+1   

这是从我的 StringIO 对象 table_data 写入 Postgres 数据库的代码:

cursor = db_connection.cursor()
cursor.copy_from(table_data, <postgres_table_name>)

【问题讨论】:

  • 请出示您的代码
  • 您使用的是哪个 MySQL 包装器?
  • 另外,不要显示“我的一些代码”,而是创建一个自包含的minimal, complete, verifiable example 并在此处发布。
  • 我使用 MySQLdb 作为 python 包从 MySQL 中检索数据
  • 同时,在您的更新和回复之后,仍然没有 MCVE,并且其他关键信息仅在 cmets 中可见,这意味着任何人搜索都是因为他们想帮助像您这样的人或因为他们有类似的问题不会看到它。

标签: python postgresql python-2.7 encoding utf


【解决方案1】:

问题是您在 str 对象上调用 encode

str 是一个字节字符串,通常表示以某种方式编码的文本,例如 UTF-8。当您为此调用encode 时,首先必须将其解码回文本,以便可以重新编码文本。默认情况下,Python 通过调用s.decode(sys.getgetdefaultencoding()) 来实现这一点,而getdefaultencoding() 通常返回'ascii'

所以,您说的是 UTF-8 编码文本,将其解码为 ASCII,然后将其重新编码为 UTF-8。

一般的解决方案是用正确的编码显式调用decode,而不是让Python使用默认值,然后encode结果。

但是当正确的编码已经是您想要的编码时,更简单的解决方案是跳过.decode('utf-8').encode('utf-8'),直接使用UTF-8 str作为UTF-8 str

或者,或者,如果您的 MySQL 包装器具有允许您指定编码并返回 unicode 值的 CHAR/VARCHAR/TEXT 列而不是 str 值(例如,在MySQLdb,您将use_unicode=True 传递给connect 调用,或者charset='UTF-8',如果您的数据库太旧而无法自动检测它),就这样做。然后您将拥有unicode 对象,您可以在它们上调用.encode('utf-8')

一般来说,处理 Unicode 问题的最佳方法是最后一种——尽可能早地解码所有内容,使用 Unicode 进行所有处理,然后尽可能晚地进行编码。但无论哪种方式,你都必须保持一致。不要打电话给str 可能是unicode;不要将 str 文字连接到 unicode 或将其传递给 replace 方法;等等。任何时候你混合和匹配,Python 都会为你隐式转换,使用你的默认编码,这几乎不是你想要的。

附带说明,这是 Python 3.x 的 Unicode 更改可以帮助解决的众多问题之一。首先,str 现在是 Unicode 文本,而不是编码字节。更重要的是,如果您个编码字节,例如,在bytes 对象中,调用encode 将给您一个AttributeError,而不是尝试静默解码以便它可以重新编码。而且,类似地,尝试混合和匹配 Unicode 和字节会给你一个明显的TypeError,而不是在某些情况下成功的隐式转换,并给出一个关于你在其他情况下没有要求的编码或解码的神秘信息。

【讨论】:

  • 我尝试在我的 MYSQL DB 连接中使用 use_unicode=True 传递 charset='UTF-8'。还意识到源 MySQL 数据库中的一些表是 latin1_swedish_ci 而有些是 utf_8。我现在收到这样的错误: db_connection = MySQLdb.connect(host=host,user=user,passwd=passwd,db=db, charset="utf8", use_unicode=True) cell = str(cell).replace(" \r", " ").replace("\n", " ").replace("\t", '').replace("\"", "") UnicodeEncodeError: 'ascii' codec can't encode第 47 位的字符 u'\u2019':序数不在范围内(128)
  • @user3422637: 好的,如果你想使用unicode 对象而不是str——这是个好主意——那么你不能对它们调用str,因为那样会立即尝试将它们编码为您的默认字符集。而且您不应该像您正在做的那样混合和匹配unicodestr 对象,因为这也必须隐式编码或解码一个或另一个;使用replace(u"\r", u" ") 等。 (实际上,"\r" 在另一种方面是不好的——如果你有反斜杠,要么转义它们,要么使用原始字符串文字。)
  • @user3422637:更一般地说,如果你在没有真正理解它的情况下试图抨击它们,你只会越来越迷失。如果您还没有阅读Unicode HOWTO,请阅读。
  • @user3422637:最后,根据您的 MySQL 和 MySQLdb 版本,您可能仍然对那些 latin1_swedish_ci 表有问题(对于较新的版本,如果您不使用 @987654362,问题可能会消失@ 并让它自动检测,但他们可能不会)。但是不要担心,除非/直到你到达那里;首先让您的代码使用 UTF-8 表,然后在瑞典语表上进行测试。
  • 非常感谢您一直以来的帮助。所以,我最终发现,我需要以 str 格式写入数据,因为我将它写入 StringIO 对象(然后使用 copy_from 将数据复制到 Postgres)。但是,我必须处理 str() 函数无法处理的所有 unicode 字符,例如 u'u\2018'、u'\xc9' 等。因此,我导入了 unicode python 包并使用 unicode() 函数将所有这些 un​​icode 字符转换为关闭的 ASCII 字符。然后我做了一个 str() 。这解决了我的问题。谢谢。
猜你喜欢
  • 1970-01-01
  • 2011-05-13
  • 2014-02-19
  • 2018-07-26
  • 2020-11-06
  • 2014-08-12
  • 2018-05-13
  • 2013-09-20
  • 2016-07-05
相关资源
最近更新 更多