gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 22:26:42 27 4
gpt4 key购买 nike

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

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

我看到的唯一答案是 here :

# Read in the file once and build a list of line offsets
line_offset = []
offset = 0
for line in file:
line_offset.append(offset)
offset += len(line)
file.seek(0)

# Now, to skip to line n (with the first line being line 0), just do
file.seek(line_offset[n])

但是,我不确定 offset += len(line) 是否操作将花费不必要的时间。有没有更直接的方法来做到这一点?

更新:

我已经做了一些计时,但似乎 .readline()比使用文件对象作为迭代器慢得多,在 python 2.7.3 .我使用了以下代码
#!/usr/bin/python

from timeit import timeit

MAX_LINES = 10000000

# use file object as iterator
def read_iter():
with open('tweets.txt','r') as f:
lino = 0
for line in f:
lino+=1
if lino == MAX_LINES:
break

# use .readline()
def read_readline():
with open('tweets.txt', 'r') as f:
lino = 0
for line in iter(f.readline,''):
lino+=1
if lino == MAX_LINES:
break

# use offset+=len(line) to simulate f.tell() under binary mode
def read_iter_tell():
offset = 0
with open('tweets.txt','rb') as f:
lino = 0
for line in f:
lino+=1
offset+=len(line)
if lino == MAX_LINES:
break

# use f.tell() with .readline()
def read_readline_tell():
with open('tweets.txt', 'rb') as f:
lino = 0
for line in iter(f.readline,''):
lino+=1
offset = f.tell()
if lino == MAX_LINES:
break

print ("iter: %f"%timeit("read_iter()",number=1,setup="from __main__ import read_iter"))
print ("readline: %f"%timeit("read_readline()",number=1,setup="from __main__ import read_readline"))
print ("iter_tell: %f"%timeit("read_iter_tell()",number=1,setup="from __main__ import read_iter_tell"))
print ("readline_tell: %f"%timeit("read_readline_tell()",number=1,setup="from __main__ import read_readline_tell"))

结果是这样的:
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() 文档将其称为“隐藏的预读缓冲区”)。

    要回答您的另一个问题,费用为:
    offset += len(line)

    是微不足道的 - 但是,如前所述,“解决方案”存在实际问题。

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

    更多细节

    实际上有几层缓冲正在进行。在硬件方面,您的磁盘驱动器内部有内存缓冲区。除此之外,您的操作系统也维护内存缓冲区,并且通常会在您以统一模式访问文件时尝试变得“智能”,要求磁盘驱动器在您正在读取的方向上“提前读取”磁盘块,超出您已经要求的块。

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

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

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

    通过这样做“一次一个字节”,C 级库为 f.tell() 维护了正确的结果。 .但要付出代价:获得的每个字节可能会有多层函数调用。

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

    所以,确实如此, for line in file:通常是逐行浏览整个文本文件的最快方法。

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

    如果事实证明这确实很重要,并且您的数据和操作系统是这样的,您可以以二进制模式打开文件并仍然获得正确的结果,那么您找到的代码片段将提供两全其美(最快的行迭代和正确的字节位置供以后使用 .seek() 'ing)。

    关于python - 在python中,可以使用缓冲区遍历大型文本文件并同时获得正确的文件位置吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19472441/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com