Python随机访问文件

21

有没有一种Python文件类型可以访问随机行而不必遍历整个文件?我需要在一个大文件中进行搜索,读取整个文件到内存中是不可能的。

任何类型或方法都将不胜感激。


1
你能解释一下为什么你想要读取随机行,而不是分部分地读取整个文件吗? - Alphaneo
3
在文件中进行二分查找,因为每行都以一个整数开始。我希望把这些整数当作键,把行中剩余的部分当作值。 - Mantas Vidutis
7个回答

21

看起来像是 mmap 设计的典型应用场景。一个 mmap 对象可以创建一个类似于字符串的接口来操作文件:

>>> f = open("bonnie.txt", "wb")
>>> f.write("My Bonnie lies over the ocean.")
>>> f.close()
>>> f.open("bonnie.txt", "r+b")
>>> mm = mmap(f.fileno(), 0)
>>> print mm[3:9]
Bonnie

如果你想知道,mmap对象也可以被赋值:

>>> print mm[24:]
ocean.
>>> mm[24:] = "sea.  "
>>> print mm[:]
My Bonnie lies over the sea.  

1
但是 mmap 不也将整个文件加载到内存中吗? mm = mmap(f.fileno(), 0)(据我所知)会将整个文件读入内存。您能详细说明一下吗? - Oleg
1
@Oleg 我的理解是它并不会将整个文件加载到内存中,而是通过虚拟内存机制使硬盘可寻址,就像它是RAM一样。我不知道所有的细节,但它绝对不会一开始就将整个文件加载到内存中,正如维基百科上的Mmap文章所解释的 - senderle

9
你可以使用 linecache 来实现:
import linecache
print linecache.getline(your_file.txt, randomLineNumber) # Note: first line is 1, not 0

这太酷了!而且它是一个内置的Python模块!谢谢! - Krzysztof Przygodzki

6

由于行的长度可以是任意的,因此您无法在不遍历整个文件的情况下获取随机行(无论您是指“其编号实际上是随机的行”还是“我选择的任意编号的行”)。

如果您只需要类似随机的效果,则可以将光标定位到文件中的一个随机位置,然后向前读取,直到遇到行终止符。但如果您想找到(比如)第1234行,则这种方法就没有用了,并且如果您真的想要一个随机选择的行,则会对行进行非均匀采样。


我看到你想要进行二分查找。在这种情况下:是的,寻找当前下限和上限之间大约中间位置的点,读取一个块,并查找该块内的行。(这可能很明显,但是:除非它们非常长,否则不要尝试以单个行的粒度随机查找。阅读更大的块。这样,您就不太依赖于操作系统和Python缓冲区的细节,并且可以避免一些开销。) - Gareth McCaughan

2

文件对象具有seek方法,可以将文件指针定位到特定的字节位置。 为了遍历大文件,可以迭代文件并在每行中检查该值。迭代文件对象不会将整个文件内容加载到内存中。


1

是的,你可以很容易地获取一条随机行。只需在文件中搜索到随机位置,然后向开头搜索直到遇到 \n 或文件开头,然后读取一行。

代码:

import sys,random
with open(sys.argv[1],"r") as f:
    f.seek(0,2)                 # seek to end of file
    bytes = f.tell()
    f.seek(int(bytes*random.random()))

    # Now seek forward until beginning of file or we get a \n
    while True:
        f.seek(-2,1)
        ch = f.read(1)
        if ch=='\n': break
        if f.tell()==1: break

    # Now get a line
    print f.readline()

1
一些注意事项:(1)如果你要在文件中进行查找,最好将其以二进制文件的形式打开。(2)如果你从着陆点向前查找,可能会获得更好的性能(因为与底层缓冲交互更好,且调用f.seek的次数较少)。不过这样做的缺点是你永远无法获取第一行,并且可能会遇到文件末尾,但你可以通过以下方式解决:如果你遇到文件末尾,回到开头并从那里读取一行。 - Gareth McCaughan
真的。向前走略微提高了性能,但是您需要环绕逻辑。我大多数开发工作都在Unix上进行,那里“二进制”和“文本”文件之间的区别没有意义。 - vy32

1

文件对象支持 seek,但请确保以二进制形式打开它们,即 "rb"。

如果数据已经处于内部格式中,则您可能还希望使用 mmap 模块进行随机访问。


1

记录长度固定吗?如果是,那么可以使用寻址实现二分查找算法。

否则,将文件加载到SQLlite数据库中。查询该数据库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接