file.tell()不一致

nig*_*udy 40 python file buffering python-2.7

有没有人碰巧知道为什么当你以这种方式迭代文件时:

输入:

f = open('test.txt', 'r')
for line in f:
    print "f.tell(): ",f.tell()
Run Code Online (Sandbox Code Playgroud)

输出:

f.tell(): 8192
f.tell(): 8192
f.tell(): 8192
f.tell(): 8192
Run Code Online (Sandbox Code Playgroud)

我一直从tell()获取错误的文件索引,但是,如果我使用readline,我会得到适当的tell()索引:

输入:

f = open('test.txt', 'r')
while True:
    line = f.readline()
    if (line == ''):
        break
    print "f.tell(): ",f.tell()
Run Code Online (Sandbox Code Playgroud)

输出:

f.tell(): 103
f.tell(): 107
f.tell(): 115
f.tell(): 124
Run Code Online (Sandbox Code Playgroud)

我正在运行python 2.7.1 BTW.

Mar*_*ers 60

使用打开的文件作为迭代器使用预读缓冲区来提高效率.结果,当您遍历行时,文件指针在文件中大步前进.

文件对象文档:

为了使for循环成为循环文件行的最有效方式(一种非常常见的操作),该next()方法使用隐藏的预读缓冲区.由于使用预读缓冲区,next()与其他文件方法(如readline())结合使用是行不通的.但是,使用seek()将文件重新定位到绝对位置将刷新预读缓冲区.

如果需要依赖.tell(),请不要将文件对象用作迭代器.您可以转而.readline()使用迭代器(以某些性能损失为代价):

for line in iter(f.readline, ''):
    print f.tell()
Run Code Online (Sandbox Code Playgroud)

这使用iter()function sentinel参数将任何可调用的函数转换为迭代器.

  • @TomDalton:您无法在Windows平台上执行此操作,因为行分隔符已被翻译.读取一行,为磁盘上的每个x + 1字节提供x个字符.当使用`io.open()`时磁盘上的多字节字符被解码为一个unicode代码点时,这也不起作用.幸运的是,`io.open()`文件对象不需要在这个答案中提供的解决方法. (4认同)
  • 补充一下,如果您知道文件从何处开始(例如从0或先前的“ seek()”开始),则可以手动跟踪文件位置,而不必使用“ tell()”。只需增加a通过从“ next()”中读取的每一行的长度来对计数器进行计数。 (2认同)

NPE*_*NPE 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;
}
Run Code Online (Sandbox Code Playgroud)

如您所见,file迭代器接口以8KB的块读取文件.这解释了为什么f.tell()它的行为方式.

文档表明它是出于性能原因而完成的(并且不保证预读缓冲区的任何特定大小).