UnicodeDecodeError: 'ascii'编解码器无法解码第47个字节为0x92的字符:该数字不在128的范围内。

3
我正在尝试使用Python将数据写入StringIO对象,最终使用psycopg2的copy_from()函数将该数据加载到postgres数据库中。起初我这样做时,copy_from()会抛出错误:ERROR: invalid byte sequence for encoding "UTF8": 0xc92,所以我遵循了this question。我发现我的Postgres数据库具有UTF8编码。我将要写入数据的文件/StringIO对象显示其编码如下:setgid Non-ISO extended-ASCII English text, with very long lines, with CRLF line terminators。我尝试将我写入到中间文件/StringIO对象的每个字符串都编码为UTF8格式。为此,我使用了.encode(encoding='UTF-8',errors='strict'))对每个字符串进行编码。现在我得到的错误是:UnicodeDecodeError: 'ascii' codec can't decode byte 0x92 in position 47: ordinal not in range(128)。这是什么意思?我该如何解决?编辑:我正在使用Python 2.7,代码的一些片段如下:
我从一个使用MySQL Workbench编码为UTF-8的MySQL数据库中读取数据。 以下是将我的数据(从MySQL数据库获取)写入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>)

请展示你的代码。 - Craig Ringer
你使用的是哪个 MySQL 包装器? - abarnert
此外,不要展示“我的一些代码片段”,请创建一个自包含的最小完整可验证示例,并在此处发布。 - abarnert
我正在使用MySQLdb作为Python包从MySQL中检索数据。 - user3422637
与此同时,在您的更新和回应之后,仍然没有提供最小可复现示例(MCVE),而且其他关键信息只能在评论中看到,这意味着任何想要帮助像您一样的人或有类似问题的人搜索不会看到它。 - abarnert
显示剩余2条评论
1个回答

7
问题在于你正在调用一个 str 对象的encode方法。 str 是一个字节字符串,通常表示以某种方式编码的文本,如 UTF-8。当你在其上调用 encode 时,它首先必须被解码回文本,以便可以重新进行编码。默认情况下,Python 通过调用 s.decode(sys.getdefaultencoding()) 进行解码,而 getdefaultencoding() 通常返回 'ascii'
因此,你正在处理 UTF-8 编码的文本,将其解码为 ASCII,然后再将其重新编码为 UTF-8。
一般的解决方案是显式地使用正确的编码调用 decode,而不是让 Python 使用默认值,然后对结果进行 encode
但是当正确的编码已经是你想要的编码时,更简单的解决方案是跳过 .decode('utf-8').encode('utf-8'),直接使用 UTF-8 str 作为它已经是的 UTF-8 str
或者,如果你的 MySQL 包具有一个特性,允许你指定一个编码并获得 CHAR/VARCHAR/TEXT 列的 unicode 值而不是 str 值(例如,在 MySQLdb 中,你可以在 connect 调用中传递 use_unicode=True,或者如果你的数据库太旧无法自动检测,则传递 charset='UTF-8'),那么就这样做。然后你将拥有 unicode 对象,并且可以在它们上调用 .encode('utf-8')
通常来说,处理 Unicode 问题的最佳方法是最后一个——尽早将所有内容解码为 Unicode,在 Unicode 中进行所有处理,然后尽可能晚地进行编码。但无论哪种方式,你都必须保持一致。不要对可能是 unicode 的东西调用 str;不要将 str 文字与 unicode 进行拼接或将其传递给其 replace 方法;等等。任何时候你混合和匹配,Python 都会隐式地使用你的默认编码进行转换,而这几乎永远不是你想要的。

顺便提一下,这是Python 3.x的Unicode变化中的许多事情之一。首先,str现在是Unicode文本,而不是编码的字节。更重要的是,如果你有编码的字节,例如在一个bytes对象中,则调用encode会给出AttributeError,而不是尝试默默地解码以便重新编码。同样,试图混合和匹配Unicode和字节将给出明显的TypeError,而不是在某些情况下成功执行隐式转换,并在其他情况下给出有关你没有请求的编码或解码的神秘消息。


我尝试在MYSQL DB连接中传递charset ='UTF-8'并使用use_unicode = True。同时意识到源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 character u'\u2019' in position 47: ordinal not in range(128) - user3422637
@user3422637:好的,如果你想使用unicode对象而不是str——这是个好主意——那么你不能调用str,因为它会立即尝试将其编码为默认字符集。而且你不应该像你现在这样混合使用unicodestr对象,因为这也必须隐式地编码或解码其中之一;使用replace(u"\r", u" ")等方法。(实际上,"\r"还有另一种坏处——如果你有反斜杠,请转义它们,或者使用原始字符串字面量。) - abarnert
更普遍地说,如果你试图在不真正理解它的情况下强行运作事物,那么你只会越来越迷失。如果你还没有阅读过Unicode HOWTO,请先阅读一下。 - abarnert
1
非常感谢您一直以来的帮助。所以,我最终发现,我需要将数据以str格式编写,因为我要将它写入StringIO对象(然后使用copy_from将数据复制到Postgres)。 然而,我必须处理所有那些无法通过str()函数处理的unicode字符,例如u'u\2018',u'\xc9'等。 因此,我导入了unicode python包,并使用unicode()函数将所有这些unicode字符转换为最接近的ASCII字符。 然后我对其进行了一个str()。 这解决了我的问题。 谢谢。 - user3422637
@user3422637:这个评论里有很多混淆的地方。我不知道你导入了什么unicode包,但你不应该需要这样的东西。我也不知道为什么你要在任何东西上调用str。之前,你已经有了一个str,所以这是无意义的。现在,你有了一个unicode,将其编码为str的方法是使用encode方法。而且,你绝对不必放弃重音符号、卷曲引号、表情符号等,只为了将数据从一个数据库复制到另一个数据库。请再次阅读HOWTO。 - abarnert
显示剩余2条评论

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