为什么 file.tell() 会影响编码?

mfm*_*ain 5 file-io character-encoding python-3.x gbk

调用tell()在阅读我的GBK编码的文件会导致下一次调用readline()来养UnicodeDecodeError。但是,如果我不调用tell(),则不会引发此错误。

C:\tmp>hexdump badtell.txt

000000: 61 20 6B 0D 0A D2 BB B0-E3                       a k......
Run Code Online (Sandbox Code Playgroud)

C:\tmp> 输入 test.py

with open(r'c:\tmp\badtell.txt', "r", encoding='gbk') as f:
    while True:
        pos = f.tell()
        line = f.readline();
        if not line: break
        print(line)
Run Code Online (Sandbox Code Playgroud)

C:\tmp>python test.py

a k

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    line = f.readline();
UnicodeDecodeError: 'gbk' codec can't decode byte 0xd2 in position 0:  incomplete multibyte sequence
Run Code Online (Sandbox Code Playgroud)

当我删除该f.tell()语句时,它成功解码。为什么?我在Win7/Win10上试过Python3.4/3.5 x64,都是一样的。

任何人,任何想法?我应该报告错误吗?

我有一个大文本文件,我真的很想获得这个大文本的文件位置范围,有解决方法吗?

Dan*_*etz 2

我刚刚在 Linux 上的 Python 3.4 x64 上复制了这个。查看 的文档TextIOBase,我没有看到任何说会tell()导致读取文件出现问题的内容,所以也许这确实是一个错误。

b'\xd2'.decode('gbk')
Run Code Online (Sandbox Code Playgroud)

给出类似于您所看到的错误,但在您的文件中该 byte 后面跟着 byte BB,并且

b'\xd2\xbb'.decode('gbk')
Run Code Online (Sandbox Code Playgroud)

给出的值等于'\u4e00',而不是错误。

我找到了一种解决方法,适用于您最初问题中的数据,但不适用于其他数据,正如您后来发现的那样。希望我知道为什么!我seek()在 every 之后调用tell(),返回的值是tell():

pos = f.tell()
f.seek(pos)
line = f.readline()
Run Code Online (Sandbox Code Playgroud)

的替代方法f.seek(f.tell())是使用SEEK_CUR的模式seek()来给出位置。如果偏移量为 0,则与上面的代码执行相同的操作:移动到当前位置并获取该位置。

pos = f.seek(0, io.SEEK_CUR)
line = f.readline()
Run Code Online (Sandbox Code Playgroud)