当你以这种方式迭代文件时,是否有人碰巧知道原因:
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。
答案 0 :(得分:59)
使用打开文件作为迭代器使用预读缓冲区来提高效率。因此,当您遍历这些行时,文件指针会在文件中大步前进。
来自File Objects文档:
为了使for循环成为循环文件行的最有效方式(一种非常常见的操作),
next()
方法使用隐藏的预读缓冲区。使用预读缓冲区的结果是,将next()
与其他文件方法(如readline()
)组合不起作用。但是,使用seek()
将文件重新定位到绝对位置将刷新预读缓冲区。
如果需要依赖.tell()
,请不要将文件对象用作迭代器。您可以将.readline()
转换为迭代器(代价是性能损失):
for line in iter(f.readline, ''):
print f.tell()
这使用iter()
function sentinel
参数将任何可调用对象转换为迭代器。
答案 1 :(得分:12)
答案在于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
的迭代器接口以8KB的块读取文件。这解释了为什么f.tell()
的行为方式。
文档suggests由于性能原因而完成(并且不保证预读缓冲区的任何特定大小)。
答案 2 :(得分:0)
我遇到了相同的预读缓冲区问题,并使用Martijn's suggestion解决了该问题。
此后,我将我的解决方案推广给其他希望做这种事情的人:
https://github.com/loisaidasam/csv-position-reader
快乐的CSV解析!