Несоответствие file.tell()
Кто-нибудь знает, почему, когда вы перебираете файл таким образом:
Вход:
f = open('test.txt', 'r')
for line in f:
print "f.tell(): ",f.tell()
Выход:
f.tell(): 8192
f.tell(): 8192
f.tell(): 8192
f.tell(): 8192
Я постоянно получаю неверный индекс файла от tell(), однако, если я использую readline, я получаю соответствующий индекс для tell():
Вход:
f = open('test.txt', 'r')
while True:
line = f.readline()
if (line == ''):
break
print "f.tell(): ",f.tell()
Выход:
f.tell(): 103
f.tell(): 107
f.tell(): 115
f.tell(): 124
Я запускаю python 2.7.1 BTW.
Ответы
Ответ 1
Использование открытых файлов в качестве итератора использует буфер чтения для повышения эффективности. В результате указатель файла продвигается большими шагами по файлу, когда вы перебираете строки.
В документации File Objects:
Чтобы сделать цикл for наиболее эффективным способом петли над строками файла (очень общая операция), метод next()
использует скрытый буфер чтения. В результате использования буфера чтения, объединение next()
с другими файловыми методами (например, readline()
) не работает правильно. Однако использование seek()
для перестановки файла в абсолютную позицию приведет к сбросу буфера чтения.
Если вам нужно полагаться на .tell()
, не используйте файл-объект как итератор. Вместо этого вы можете превратить .readline()
в итератор (по цене некоторой потери производительности):
for line in iter(f.readline, ''):
print f.tell()
Здесь используется аргумент iter()
sentinel
, чтобы превратить любое вызываемое в итератор.
Ответ 2
Ответ заключается в следующей части исходного кода Python 2.7 (fileobject.c
):
#define READAHEAD_BUFSIZE 8192
static PyObject *
file_iternext(PyFileObject *f)
{
PyStringObject* l;
if (f->f_fp == NULL)
return err_closed();
if (!f->readable)
return err_mode("reading");
l = readahead_get_line_skip(f, 0, READAHEAD_BUFSIZE);
if (l == NULL || PyString_GET_SIZE(l) == 0) {
Py_XDECREF(l);
return NULL;
}
return (PyObject *)l;
}
Как вы можете видеть, file
интерфейс итератора считывает файл в блоках по 8 КБ. Это объясняет, почему f.tell()
ведет себя так, как это делает.
Документация предлагает ее по соображениям производительности (и не гарантирует какой-либо конкретный размер буфера readahead).
Ответ 3
Я столкнулся с той же проблемой буфера для чтения и решил ее с помощью предложения Мартина.
С тех пор я обобщил свое решение для тех, кто хочет делать такие вещи:
https://github.com/loisaidasam/csv-position-reader
Счастливый разбор CSV!