【问题标题】:Python Unreproducible UnicodeDecodeErrorPython不可重现的UnicodeDecodeError
【发布时间】:2018-04-20 09:33:05
【问题描述】:

我正在尝试使用 Python 中的以下命令序列替换 Word 文件中的子字符串。单独的代码工作得非常好 - 即使使用完全相同的 Word 文件,但是当将其嵌入到更大规模的项目结构中时,它会在该位置引发错误。我对导致它的原因一无所知,因为它似乎与代码无关,而且对我来说似乎无法重现。

旁注:我知道导致错误的原因,它是 Word 文件中的德语“ü”,但如果代码独立运行,则需要它并且删除它似乎不是正确的解决方案。

#foo.py
from bar import make_wordm
def main(uuid):
    with open('foo.docm', 'w+') as f:
        f.write(make_wordm(uuid=uuid))

main('1cb02f34-b331-4616-8d20-aa1821ef0fbd')

foo.py 导入 bar.py 来完成繁重的工作。

#bar.py
import tempfile
import shutil
from cStringIO import StringIO
from zipfile import ZipFile, ZipInfo

WORDM_TEMPLATE='./res/template.docm'
MODE_DIRECTORY = 0x10

def zipinfo_contents_replace(zipfile=None, zipinfo=None,
                             search=None, replace=None):
    dirname = tempfile.mkdtemp()
    fname = zipfile.extract(zipinfo, dirname)
    with open(fname, 'r') as fd:
        contents = fd.read().replace(search, replace)
    shutil.rmtree(dirname)
    return contents

def make_wordm(uuid=None, template=WORDM_TEMPLATE):
    with open(template, 'r') as f:
        input_buf = StringIO(f.read())
    output_buf = StringIO()
    output_zip = ZipFile(output_buf, 'w')

    with ZipFile(input_buf, 'r') as doc:
        for entry in doc.filelist:
            if entry.external_attr & MODE_DIRECTORY:
                continue

            contents = zipinfo_contents_replace(zipfile=doc, zipinfo=entry,
                                        search="00000000-0000-0000-0000-000000000000"
                                        , replace=uuid)
            output_zip.writestr(entry, contents)
    output_zip.close()
    return output_buf.getvalue()

在更大规模的上下文中嵌入相同的代码时会引发以下错误:

ERROR:root:message
Traceback (most recent call last):
  File "FooBar.py", line 402, in foo_bar
    bar = bar_constructor(bar_theme,bar_user,uuid)
  File "FooBar.py", line 187, in bar_constructor
    if(main(uuid)):
  File "FooBar.py", line 158, in main
    f.write(make_wordm(uuid=uuid))
  File "/home/foo/FooBarGen.py", line 57, in make_wordm
    search="00000000-0000-0000-0000-000000000000", replace=uuid)
  File "/home/foo/FooBarGen.py", line 24, in zipinfo_contents_replace
    contents = fd.read().replace(search, replace)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2722: ordinal not in range(128)
INFO:FooBar:None

编辑:经过进一步检查和调试,似乎是变量 'uuid' 导致了问题。当将参数作为全文字符串 ('1cb02f34-b331-4616-8d20-aa1821ef0fbd') 提供时,而不是使用从 JSON 解析的变量,它可以正常工作。

edit2:我必须添加uuid = uuid.encode('utf-8', 'ignore'),现在它工作得很好。

【问题讨论】:

  • 在您的open 上,您没有指定编码。如果它在某个时候起作用,并不意味着它是正确的。 Python 口头禅:“显式优于隐式”。注意:大型框架可以激活“语言环境”,因此它可以改变本地部分的行为(以及 ev. translate 也可以)

标签: python unicode ascii decode


【解决方案1】:

问题在于混合 Unicode 和字节字符串。 Python 2 “有帮助”尝试从一种转换到另一种,但默认使用 ascii 编解码器。

这是一个例子:

>>> 'aeioü'.replace('a','b')  # all byte strings
'beio\xfc'
>>> 'aeioü'.replace(u'a','b') # one Unicode string and it converts...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xfc in position 4: ordinal not in range(128)

您提到从 JSON 读取 UUID。 JSON 返回 Unicode 字符串。理想情况下,读取所有解码为 Unicode 的文本文件,以 Unicode 进行所有文本处理,并在写回存储时对文本文件进行编码。在您的“更大的框架”中,这可能是一项大型移植工作,但本质上是使用带有编码的io.open 来读取文件并解码为 Unicode:

with io.open(fname, 'r', encoding='utf8') as fd:
    contents = fd.read().replace(search, replace)

请注意,encoding 应该与您正在阅读的文件的实际编码相匹配。这是你必须确定的。

正如您在编辑中发现的那样,一种快捷方式是将 UUID 从 JSON 编码回字节字符串,但目标应该是使用 Unicode 处理文本。

Python 3 通过默认将字符串设为 Unicode 来清理此过程,并放弃与字节/Unicode 字符串的隐式转换。

【讨论】:

    【解决方案2】:

    改变这一行:

    with open(fname, 'r') as fd:
    

    到这里:

    with open(fname, 'r', encoding='latin1') as fd:
    

    ascii 编码可以处理 0 到 127 之间的字符代码。您的文件包含超出范围的字符代码 0xc3。您需要选择不同的编解码器。

    【讨论】:

    • 谢谢!这是针对 Python 3 的吗?尝试使用时获取'encoding' is an invalid keyword argument for this function。除此之外,为什么只使用代码 sn-ps 时没有抛出 UnicodeDecodeError 呢?编辑:要在 Python2.7 中使用编码选项,请使用 io.open - 我仍然得到UnicodeEncodeError: 'ascii' codec can't encode characters in position 2721-2722: ordinal not in range(128)
    • 即使使用contents = fd.read().decode("latin1").encode("utf8").replace(search, replace) 也会抛出相同的错误。无论如何,这似乎不是解决为什么会发生这种情况的解决方案,因为代码 sn-ps 独立工作。
    【解决方案3】:

    过去我一直遇到特殊字符问题,我通过在读取时解码为 Unicode,然后在写回文件时编码为 utf-8 来解决它们。

    我希望这对你也有用。

    对于我的解决方案,我一直使用我在此演示文稿中找到的内容 http://farmdev.com/talks/unicode/

    所以我会用这个:

    def to_unicode_or_bust(obj, encoding='utf-8'):
        if isinstance(obj, basestring):
            if not isinstance(obj, unicode):
                obj = unicode(obj, encoding)
        return obj
    

    然后在你的代码上:

    contents = to_unicode_or_bust(fd.read().replace(search, replace))
    

    然后在编写时将编码设置回 utf-8。

    output_zip.writestr(entry, contents.encode('utf-8'))
    

    我没有重现您的问题,所以这只是一个建议。希望有效

    【讨论】:

    • 感谢您的建议,但我仍然收到UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2722: ordinal not in range(128)。不幸的是,这种解决方法试图解决一个原本不应该存在的问题。
    • 你也可以试试:contents = to_unicode_or_bust(fd.read()).replace(search, replace)
    • 感谢您的努力。 UnicodeEncodeError: 'ascii' codec can't encode character u'\xfc' in position 2722: ordinal not in range(128) 我尝试了 Stackoverflow 上的所有编码/解码程序。老实说,我真的不认为这是解决方法,因为我不清楚为什么首先抛出错误,因为 code-sn-ps 工作。
    • 除了编码/解码问题之外,不能有别的问题,因为删除会使整个事情正常工作,对吗?不知道你是否在 Windows 系统上,你可能会收到this issue 如果我是你,我会在所有读取中解码并在所有写入中编码。
    • 删除 'ü' 会使整个项目在更大的项目中工作,是的 - 但保留 'ü' 并在较小的项目中专门运行相同的代码也可以,并且不会抛出编码/解码错误。我在 UNIX 上,但您链接的帖子仍然很有趣!
    猜你喜欢
    • 1970-01-01
    • 2014-12-15
    • 2020-10-07
    • 2012-03-24
    • 1970-01-01
    • 2017-03-22
    • 1970-01-01
    • 2018-02-21
    • 2013-11-21
    相关资源
    最近更新 更多