Python Unreproducible UnicodeDecodeError
我试图用下面的命令序列在python中替换Word文件中的子字符串。代码本身工作得非常好——即使使用完全相同的Word文件,但是当将它嵌入到一个更大的项目结构中时,它会在该位置抛出一个错误。我对它的起因一无所知,因为它似乎与代码无关,对我来说似乎无法产生。
旁注:我知道是什么导致了这个错误,它在Word文件中是一个德语"_",但如果代码独立工作,那么需要删除它,并且删除它似乎不是正确的解决方案。
1 2 3 4 5 6 7 | #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用于重型起重。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #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() |
在大范围上下文中嵌入同一代码时引发以下错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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解析的变量,而是非常好地工作。
伊迪丝2:我必须加上
问题是混合了Unicode和字节字符串。python 2"非常有用"尝试从一个转换到另一个,但默认使用
下面是一个例子:
1 2 3 4 5 6 | >>> '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进行所有文本处理,并在回写到存储器时对文本文件进行编码。在您的"更大的框架"中,这可能是一个大的移植作业,但实际上使用带编码的
1 2 | with io.open(fname, 'r', encoding='utf8') as fd: contents = fd.read().replace(search, replace) |
注意,
正如您在编辑中所发现的,一个快捷方式是将uuid从json编码回一个字节字符串,但是使用unicode处理文本应该是目标。
python 3通过使字符串在默认情况下为Unicode来清理这个过程,并删除到/从byte/unicode字符串的隐式转换。
在过去,我经常遇到特殊字符的问题,我通过在读取时解码为Unicode,然后在回写文件时编码为UTF-8来解决这些问题。
我也希望这对你有用。
对于我的解决方案,我总是使用在本演示文稿中找到的内容http://farmdev.com/talks/unicode/
所以我会用这个:
1 2 3 4 5 | def to_unicode_or_bust(obj, encoding='utf-8'): if isinstance(obj, basestring): if not isinstance(obj, unicode): obj = unicode(obj, encoding) return obj |
然后在你的代码上:
1 | contents = to_unicode_or_bust(fd.read().replace(search, replace)) |
然后在编写时,将编码设置回UTF-8。
1 | output_zip.writestr(entry, contents.encode('utf-8')) |
我没有重复你的问题,所以这只是一个建议。希望它能起作用
更改此行:
1 | with open(fname, 'r') as fd: |
对此:
1 | with open(fname, 'r', encoding='latin1') as fd: |
ASCII编码可以处理介于0和127之间(含0和127)的字符代码。文件包含超出范围的字符代码0xc3。您需要选择不同的编解码器。