Python file.tell()不一致

Python file.tell()不一致,python,python-2.7,file,buffering,Python,Python 2.7,File,Buffering,当您以这种方式迭代文件时,是否有人知道原因: 输入: 输出: 我总是从tell()获取错误的文件索引,但是,如果我使用readline,我会为tell()获取适当的索引: 输入: 输出: 顺便说一句,我正在运行python 2.7.1。使用打开的文件作为迭代器,使用预读缓冲区来提高效率。因此,当您在行上循环时,文件指针会在文件中以大步前进 从文件中: 为了使for循环成为在文件行上循环的最有效方式(一种非常常见的操作),next()方法使用隐藏的预读缓冲区。使用预读缓冲区的结果是,将next()

当您以这种方式迭代文件时,是否有人知道原因:

输入: 输出: 我总是从tell()获取错误的文件索引,但是,如果我使用readline,我会为tell()获取适当的索引:

输入: 输出:
顺便说一句,我正在运行python 2.7.1。

使用打开的文件作为迭代器,使用预读缓冲区来提高效率。因此,当您在行上循环时,文件指针会在文件中以大步前进

从文件中:

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

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


这将使用
sentinel
参数将任何可调用对象转换为迭代器。

答案在Python 2.7源代码的以下部分(
fileobject.c
):

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


由于性能原因(并且不保证预读缓冲区的任何特定大小)而完成的文档。

我遇到了相同的预读缓冲区问题,并使用解决方案解决了它

此后,我将我的解决方案推广到其他任何希望做此类事情的人:


祝你快乐

仅讨论所需的问题:您确定在第一个示例中没有到达文件的末尾吗?除此之外,如果您知道文件从何处开始(例如从0或以前的
seek()
),您可以手动跟踪文件位置,而不必使用
tell()
。只需按从
next()读取的每行长度增加一个计数器
。现在您有了一个“正确”的文件位置,并且readahead提高了性能。@TomDalton:在Windows平台上,由于行分隔符被转换,因此无法执行此操作。读取其中的一行将为磁盘上的每x+1字节提供x个字符。这在使用
io.open()时也不起作用
将磁盘上的多字节字符解码为一个unicode码点。幸运的是
io.open()
文件对象不需要解决这个问题。@MartijnPieters.无耻的自我宣传:。这与Py3在迭代时完全禁用
tell
的原因相同吗?@MadPhysician:不,Python 3的原因不同;
TextIO*
包装依赖于多层缓冲和支持移植tell会对性能产生严重影响。您对Python3有类似的见解吗?
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
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
for line in iter(f.readline, ''):
    print f.tell()
#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;
}