file.tell() 不一致
- 2025-04-10 09:44:00
- admin 原创
- 22
问题描述:
有人知道为什么当你以这种方式迭代文件时:
输入:
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。
解决方案 1:
使用打开的文件作为迭代器会使用预读缓冲区来提高效率。因此,当您循环遍历行时,文件指针会在文件中大步前进。
来自文件对象文档:
为了使 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
的迭代器接口以 8KB 的块为单位读取文件。这解释了为什么f.tell()
它会这样运行。
文档表明这样做是出于性能原因(并且不保证预读缓冲区的任何特定大小)。
解决方案 3:
我遇到了相同的预读缓冲区问题,并使用Martijn 的建议解决了它。
从那时起,我就将我的解决方案推广到其他想要做此类事情的人:
https://github.com/loisaidasam/csv-position-reader
祝您 CSV 解析愉快!
相关推荐
热门文章
项目管理软件有哪些?
热门标签
曾咪二维码
扫码咨询,免费领取项目管理大礼包!
云禅道AD