Python 2.7 UnicodeDecodeError: 'ascii'编解码器无法解码字节

4
我一直在解析一些docx文件(UTF-8编码的XML)中的特殊字符(捷克字母)。当我尝试输出到标准输出时,一切都很顺利,但是我无法将数据输出到文件中,会出现以下错误:
Traceback (most recent call last): File "./test.py", line 360, in ofile.write(u'\t\t\t\t\t\n') UnicodeEncodeError: 'ascii' codec can't encode character u'\xed' in position 37: ordinal not in range(128)
尽管我明确地将变量“word”转换为Unicode类型(type(word)返回的是Unicode类型),我仍然尝试使用.encode('utf-8')进行编码,但仍然卡在这个错误上。
以下是代码示例:
for word in word_list:
    word = unicode(word)
    #...
    ofile.write(u'\t\t\t\t\t<feat att="writtenForm" val="'+word+u'"/>\n')
    #...

我也尝试了以下方法:
for word in word_list:
    word = word.encode('utf-8')
    #...
    ofile.write(u'\t\t\t\t\t<feat att="writtenForm" val="'+word+u'"/>\n')
    #...

即使这两者结合起来:
word = unicode(word)
word = word.encode('utf-8')

我当时有些绝望,所以甚至尝试在ofile.write()中编码word变量。
ofile.write(u'\t\t\t\t\t<feat att="writtenForm" val="'+word.encode('utf-8')+u'"/>\n')

我希望你能给我一些提示,告诉我我做错了什么。

你尝试过其他更适合该语言的编码方式吗? - f p
如果你使用最新版本的Python,我敢打赌你不会遇到这些问题。 - Oleh Prypin
不幸的是,我使用更合适的编码方式仍然遇到了相同的错误,并且由于服务器上安装的是v2.7版本,所以无法使用最新的Python版本来运行脚本。 - gilipf
1
这个答案可能有所帮助:https://dev59.com/xnRB5IYBdhLWcg3w26t3#844443 - Lev Levitsky
4个回答

11

ofile是一个字节流,你正在将一个字符字符串写入其中。因此,它会尝试通过编码为字节字符串来处理你的错误。这只对ASCII字符通常是安全的。由于word包含非ASCII字符,所以它会失败:

>>> open('/dev/null', 'wb').write(u'ä')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 0:
                    ordinal not in range(128)

通过使用io.open打开文件,将ofile变为文本流,模式为'wt',并显式指定编码:

>>> import io
>>> io.open('/dev/null', 'wt', encoding='utf-8').write(u'ä')
1L

另外,您也可以使用codecs.open,它具有几乎相同的接口,或者使用encode手动对所有字符串进行编码。


在使用 io 输出数据时,从输入中以相同的方式读取数据是否很重要? - gilipf
@rivfaader 不,完全不是。只需确保数据仅由“unicode”对象组成即可。在Python 3.3+中运行代码可能会有所帮助,因为它不会默默地让“bytes”对象通过。 - phihag
非常感谢,我之所以问是因为即使我以正确的方式打开它,我仍然遇到了相同的错误...但我对所有变量进行了编码,现在它可以工作了,再次感谢。 - gilipf
嗨@phihag,我正在使用IOByte将文件上传到我的服务器。对于某些文件,我会收到相同的错误消息: UnicodeEncodeError:'ascii' codec can't encode character u'\xed' in position 1: ordinal not in range(128) 我该如何解决这个问题?我该如何将您在此处提到的相同方法应用于IOByte对象? - Rafael Soares - tuelho
@Tuelho 请提出一个新问题。不要忘记发布一个可重现的示例。如果您愿意,您可以通过电子邮件联系我(mailto:phihag@phihag.de),或在此处评论并附上新问题的链接。 - phihag

2

在撰写Word文档(.docx)时,我遇到了类似的错误。特别是在使用欧元符号(€)时。

x = "€".encode()

出错信息:

UnicodeDecodeError: 'ascii' 编解码器无法将第0个字节位置上的0xe2解码:128以外的值

我是这样解决的:

x = "€".decode()

我希望这能帮到您!

2

Phihag的回答是正确的。我想建议手动将Unicode转换为带有显式编码的字节字符串:

ofile.write((u'\t\t\t\t\t<feat att="writtenForm" val="' +
             word + u'"/>\n').encode('utf-8'))

也许您想了解如何使用基本机制而不是高级魔法和黑魔法(例如io.open)来完成此操作。

嗯,为什么io.open被称作高级巫术或者甚至是黑魔法呢?我相信对于每个程序员来说,理解字节流和文本流之间的区别并不难,只需拥有一个最基本的理解模型即可。 - phihag
每一门足够先进的科学都是不可区分于魔法的。 (阿瑟·克拉克) 我的意思是,你的答案通过使用一个库来解决一个特殊情况(流式传输),而你不需要理解它是如何做到这一点的(即魔法)。实际上,在更多情况下,你会遇到这种麻烦,而且通常最好知道如何通用地解决它。许多地方会自动将给定的Unicode转换为str,并在存在奇怪字符时立即引发错误。 - Alfe
在Python 3中,几乎没有自动转换。请注意,在Python 2中,io.open仅在stdlib中(而不是内置函数)中存在。在Python 3中,io.open等同于open - phihag

1

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