关于unicode:Python不可重现的UnicodeDecodeError

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:我必须加上uuid = uuid.encode('utf-8', 'ignore'),现在工作得很好。


问题是混合了Unicode和字节字符串。python 2"非常有用"尝试从一个转换到另一个,但默认使用ascii编解码器。

下面是一个例子:

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进行所有文本处理,并在回写到存储器时对文本文件进行编码。在您的"更大的框架"中,这可能是一个大的移植作业,但实际上使用带编码的io.open来读取文件并解码为unicode:

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

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

正如您在编辑中所发现的,一个快捷方式是将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。您需要选择不同的编解码器。