Python file.tell() дает странные числа?

Я использую Python 3.3.0, на Windows 64bit.

У меня есть текстовый файл, как показано ниже: (см. нижнюю часть ссылки для загрузки в mediafire)

hello

-data1:blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah


-data2:blah blah blah blah blah blah blah blah blah blah blah
-data3: Empty

-data4: Empty

Я пытаюсь перемещаться по файлу, и поэтому я использую .tell(), чтобы выяснить, какова моя позиция. Однако при чтении строк файла, как показано ниже, я получаю очень странный результат:

f=open("test.txt")
while True:
    a = f.readline()
    print("{}    {}".format(repr(a),f.tell()))
    if a == "":
        break

Результат:

'hello\n'    7
'\n'    9
'-data1:blah blah blah blah blah blah blah blah blah blah blah blah blah blah bl
ah blah\n'    18446744073709551714
'\n'    99
'\n'    101
'-data2:blah blah blah blah blah blah blah blah blah blah blah\n'    164
'-data3: Empty\n'    179
'\n'    181
'-data4: Empty'    194
''    194

Что с 18446744073709551714 для 3-й линии? Хотя это выглядит как невозможное значение, f.seek(18446744073709551714) является приемлемым значением, которое, по-видимому, приводит меня к концу третьей строки. Хотя, я не могу понять, почему.

EDIT: Открытие в двоичном режиме не вызывает проблем с tell():

f=open("test.txt","rb")
while True:
    a = f.readline()
    print("{}    {}".format(repr(a),f.tell()))
    if a == b"":
        break

Результат:

b'hello\r\n'    7
b'\r\n'    9
b'-data1:blah blah blah blah blah blah blah blah blah blah blah blah blah blah b
lah blah\r\n'    97
b'\r\n'    99
b'\r\n'    101
b'-data2:blah blah blah blah blah blah blah blah blah blah blah\r\n'    164
b'-data3: Empty\r\n'    179
b'\r\n'    181
b'-data4: Empty'    194
b''    194

Текстовый файл test.txt можно загрузить здесь, всего чуть-чуть 194 байта: http://www.mediafire.com/?1wm4lujb2j48y23

Ответ 1

Это документированное поведение, вызванное окончанием строки в стиле UNIX:

file.tell()

Верните текущую позицию файлов, например stdio ftell().

Примечание. В Windows tell() может возвращать недопустимые значения (после fgets()) при чтении файлов с использованием строк в стиле Unix. Использовать двоичный режим ('rb'), чтобы обойти эту проблему.


Вышеупомянутая документация взята из документации python2.7.4. Документация для python3 немного изменилась, так как теперь есть иерархия классов, обрабатывающих I/O, и я не могу найти этот бит информации. Ваш тест показывает, что поведение не изменилось. Также исходный код для python3.3 имеет комментарий XXX Windows support below is likely incomplete перед функцией, вызываемой tell.


В python отладчик ошибок есть issue, и последний комментарий от Catalin Iacob:

Я попытался воспроизвести это, взял файл на своем диске, и действительно, я получил отрицательное число, но этот файл имеет окончание строки Unix. Это задокументировано на http://docs.python.org/2/library/stdtypes.html#file.tellтак что, вероятно, там нечего делать.

Что касается отчета Armin в msg180145, хотя он не интуитивно понятен, это соответствует действию ftell в Windows, как описано в примечаниях раздел http://msdn.microsoft.com/en-us/library/0ys3hc0b%28v=vs.100%29.aspx. Метод tell() в файлах файлах явно документирован как совпадение поведение ftell: "Вернуть текущую позицию файлов, например, stdio's ftell()". Поэтому, хотя это и не интуитивно понятно, возможно лучше оставить его как есть. tell() возвращает интуитивный ненулевой при открытии с помощью "a" на Python3 и Python 2.7 при использовании io.open, поэтому он все равно будет исправлен.

Таким образом, это похоже на ошибку "wontfix". Кто-то должен, вероятно, открыть проблему (прокомментировал проблему), потому что этот факт вообще не упоминается в документации python3.


В соответствии с Antoine Pitrou python3 вообще не использует ftell(), поэтому это, похоже, является другой ошибкой. Также ошибка не воспроизводится в python3.2.3 и, вероятно, была введена при фиксации этого issue (по крайней мере, это единственное изменение, которое я могу найти к реализации tell() между 3.2.3 и 3.3)


Последнее редактирование: согласно документации io метод tell делает не возвращать номер байтов с начала файла. Возвращаемое значение - это "непрозрачное число", что означает, что единственный способ использовать его - передать его в seek, чтобы вернуться в эту позицию. Другие операции не имеют смысла. Тот факт, что до python3.2.3 возвращаемое значение было тем, что вы ожидали, было только деталью реализации.

Обратите внимание, что информация в этом разделе документации просто неверна и, надеюсь, она будет исправлена в будущем.