在python中,可以使用缓冲区遍历大型文本文件,同时获得正确的文件位置吗?

在python中,可以使用缓冲区遍历大型文本文件,同时获得正确的文件位置吗?,python,file-io,Python,File Io,我试图通过一个大的文本文件(~232GB)搜索一些关键字。我想利用缓冲来解决速度问题,还想记录包含这些关键字的行的起始位置 我在这里看到过很多讨论类似问题的帖子。但是,那些使用缓冲(使用文件作为迭代器)的解决方案无法给出正确的文件位置,而那些提供正确文件位置的解决方案通常只是使用不使用缓冲的f.readline() 我看到的唯一既能做到这两个方面的答案是: 但是,我不确定offset+=len(line)操作是否会花费不必要的时间。有没有更直接的方法 更新: 我已经做了一些计时,但似乎.rea

我试图通过一个大的文本文件(~232GB)搜索一些关键字。我想利用缓冲来解决速度问题,还想记录包含这些关键字的行的起始位置

我在这里看到过很多讨论类似问题的帖子。但是,那些使用缓冲(使用文件作为迭代器)的解决方案无法给出正确的文件位置,而那些提供正确文件位置的解决方案通常只是使用不使用缓冲的
f.readline()

我看到的唯一既能做到这两个方面的答案是:

但是,我不确定
offset+=len(line)
操作是否会花费不必要的时间。有没有更直接的方法

更新: 我已经做了一些计时,但似乎
.readline()
比在
python2.7.3
上使用file对象作为迭代器要慢得多。我使用了以下代码

结果是:

iter: 5.079951
readline: 37.333189
iter_tell: 5.775822
readline_tell: 38.629598

使用
.readline()
有什么问题

您发现的示例对于以文本模式打开的文件不正确。它应该可以在Linux系统上正常工作,但不能在Windows上工作。在Windows上,在文本模式文件中返回到以前位置的唯一方法是查找以下位置之一:

  • 0(文件的开头)

  • 文件结束

  • 以前由
    f.tell()
    返回的位置

  • 您不能以任何可移植的方式计算文本模式文件位置

    因此,请使用
    .readline()
    和/或
    .read()
    .tell()
    。问题已解决;-)

    关于缓冲:是否使用缓冲与文件的访问方式无关;这完全与文件的打开方式有关。缓冲是一个实现细节。特别是,
    f.readline()
    当然是在封面下缓冲的(除非您在文件
    open()
    调用中明确禁用了缓冲),但缓冲方式对您来说是不可见的。将文件对象用作迭代器时发现的问题与文件迭代器实现(file.next()docs称之为“隐藏的预读缓冲区”)添加的额外缓冲层有关

    要回答您的另一个问题,您需要支付以下费用:

    offset += len(line)
    
    是微不足道的,但是,正如前面提到的,“解决方案”有真正的问题

    短期课程:不要过早地变得棘手。做最简单有效的事情(比如
    .readline()
    +
    .tell()
    ),只有在证明不充分时才开始担心

    更多细节 实际上有好几层缓冲正在进行。在硬件方面,您的磁盘驱动器中有内存缓冲区。除此之外,您的操作系统还维护内存缓冲区,当您以统一模式访问文件时,通常会尝试“智能”,要求磁盘驱动器在您正在读取的方向上“预读”磁盘块,超出您已经要求的块

    CPython的I/O构建在平台C的I/O库之上。C库有自己的内存缓冲区。为了让Python的
    f.tell()
    能够“正常工作”,CPython必须以C指定的方式使用C库

    现在关于“一行”没有什么特别之处(好吧,在任何主要的操作系统上都没有)。“行”是一个软件概念,通常意味着“直到并包括下一个
    \n
    字节(Linux)、
    \r
    字节(某些Mac版本)或
    \r\n
    字节对(Windows)。硬件、操作系统和C缓冲区通常不知道任何有关“行”的信息-它们只处理字节流

    在封面下,Python的
    .readline()
    基本上是一次“读取”一个字节,直到它看到平台的行尾字节序列(
    \n
    \r
    ,或
    \r\n
    )“在引号中,因为通常不涉及磁盘访问-通常只是不同级别的软件从内存缓冲区复制字节。当涉及到磁盘访问时,速度要慢几千倍

    通过“一次一个字节”的操作,C级库为
    f.tell()
    维护正确的结果。但代价是:所获得的每个字节可能有多层函数调用

    Python的文件迭代器一次将字节块“读取”到自己的内存缓冲区中。“多少”无关紧要;-)重要的是,它要求C库一次复制多个字节,然后CPython通过自己的内存缓冲区搜索行尾序列。这将大幅减少所需的函数调用数。但代价不同:C库关于我们在文件中的位置的想法反映了读入文件迭代器内存缓冲区的字节数,这与用户的Python程序从该缓冲区检索到的字节数无关

    因此,的确,
    对于文件中的行:
    通常是逐行浏览整个文本文件的最快方式

    这有关系吗?唯一能确定的方法是根据真实数据对其进行计时。如果要读取一个200+GB的文件,那么进行物理磁盘读取所花费的时间将是搜索行尾字节序列所花费的软件各层时间的数千倍


    如果事实证明这很重要,并且您的数据和操作系统可以以二进制模式打开文件,并且仍然可以得到正确的结果,那么您找到的代码片段将提供这两个方面的最佳效果(最快的行迭代,以及稍后的正确字节位置
    .seek()
    ).

    试试这个:@Mai:io库在迭代时禁用
    .tell()
    。@Mai:换句话说,
    io.BufferedReader
    (以及打开文本文件时返回的
    io.TextBase
    代理)不能解决这个问题。@Martijn Pieters:谢谢您的输入。。。我建议进行一次试验。。。没有将其列为答案…@M
    iter: 5.079951
    readline: 37.333189
    iter_tell: 5.775822
    readline_tell: 38.629598
    
    offset += len(line)