带有seek()和read()的UnicodeDecodeError

时间:2013-02-13 09:56:54

标签: python file-io unicode utf-8 python-3.x

我在编程Python中遵循示例代码,并且有些混淆。这是将简单字符串写入文件然后将其读回的代码

>>> data = 'sp\xe4m'                                 # data to your script
>>> data, len(data)                                  # 4 unicode chars, 1 nonascii
('späm', 4)
>>> data.encode('utf8'), len(data.encode('utf8'))    # bytes written to file
(b'sp\xc3\xa4m', 5)
>>> f = open('test', mode='w+', encoding='utf8')     # use text mode, encoded
>>> f.write(data)
>>> f.flush()
>>> f.seek(0); f.read(1)                             # ascii bytes work
's'
>>> f.seek(2); f.read(1)                             # as does 2-byte nonascii
'ä'
>>> data[3]                                          # but offset 3 is not 'm' !
'm'
>>> f.seek(3); f.read(1)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xa4 in position 0:
unexpected code byte

现在,令我困惑的是,如果数据字符串是utf8编码的话,为什么会发生UnicodeDecodeError?使用手动f.read()读取工作正常,但使用seek跳转和读取(1)时,会出现此错误。

1 个答案:

答案 0 :(得分:0)

在文件中查找会将读指针移动 bytes ,而不是字符。 .read()调用期望能够读取整个字符。因为UTF-8对ASCII字符集之外的任何unicode代码点使用多个字节,所以不能只是寻找多字节UTF-8代码点的中间位置并期望.read()能够工作。

U + 00a4代码点(字形ä)被编码为两个字节,C3和A4。在文件中,这意味着现在有5个字节,表示sp,十六进制字节C3和A4,然后是m

通过寻找位置3,您将文件头移动到A4字节,然后调用.read()失败,因为没有前面的C3字节,没有足够的上下文来解码字符。这提高了UnicodeDecodeError; A4字节是意外的,因为它不是有效的UTF-8序列。

寻找位置4:

>>> f.seek(3); f.read(1)
'm'

更好的是,不要寻找UTF-8数据,或者以二进制模式打开文件并手动解码。