Python不可复制的UnicodeDecodeError

时间:2018-04-20 09:33:06

标签: python unicode ascii decode

我正在尝试使用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'),现在它的效果非常好。

3 个答案:

答案 0 :(得分: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字符串。

答案 1 :(得分:0)

更改此行:

SUM()

到此:

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

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

答案 2 :(得分:0)

过去我遇到过特殊字符的问题我已经解决了这些问题,在阅读时解码为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'))

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