Почему file.tell() влияет на кодировку?

Призвание tell() при чтении моего файла в кодировке GBK вызывает следующий вызов readline() поднять UnicodeDecodeError, Однако, если я не позвоню tell(), это не вызывает эту ошибку.

C: \ tmp> hexdump badtell.txt

000000: 61 20 6B 0D 0A D2 BB B0-E3                       a k......

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)

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

Когда я удаляю f.tell() Заявление, оно успешно расшифровано. Зачем? Я пробовал Python3.4/3.5 x64 на Win7/Win10, это все то же самое.

Любая, любая идея? Должен ли я сообщить об ошибке?

У меня большой текстовый файл, и я действительно хочу получить диапазоны позиций файла этого большого текста, есть ли обходной путь?

2 ответа

ОК, есть обходной путь, он работает до сих пор:

with open(r'c:\tmp\badtell.txt', "rb") as f:
    while True:
        pos = f.tell()
        line = f.readline();
        if not line: break
        line = line.decode("gbk").strip('\n')
        print(line)

Я отправил вопрос вчера здесь: http://bugs.python.org/issue26990

до сих пор нет ответа

Я только что повторил это на Python 3.4 x64 на Linux. Глядя на документы для TextIOBase Я не вижу ничего, что говорит tell() вызывает проблемы с чтением файла, так что, возможно, это действительно ошибка.

b'\xd2'.decode('gbk')

выдает ошибку, подобную той, которую вы видели, но в вашем файле за байтом следует байт BB, а также

b'\xd2\xbb'.decode('gbk')

дает значение, равное '\u4e00' не ошибка.

Я нашел обходной путь, который работает для данных в исходном вопросе, но не для других данных, как вы с тех пор нашли. Хотел бы я знать почему! я звонил seek() после каждого tell() со значением, которое tell() вернулся:

pos = f.tell()
f.seek(pos)
line = f.readline()

Альтернатива f.seek(f.tell()) это использовать SEEK_CUR режим seek() дать позицию. Со смещением 0 это делает то же самое, что и код выше: перемещается в текущую позицию и получает эту позицию.

pos = f.seek(0, io.SEEK_CUR)
line = f.readline()
Другие вопросы по тегам