如何从python中的一个文件中读取随机行?

Sha*_*ane 33 python

有内置的方法吗?如果不能,如何在不花费太多开销的情况下做到这一点?

Ale*_*lli 63

不是内置的,但R(3.4.2)Knuth的"计算机编程艺术"中的算法(Waterman的"水库算法")很好(在一个非常简化的版本中):

import random

def random_line(afile):
    line = next(afile)
    for num, aline in enumerate(afile, 2):
      if random.randrange(num): continue
      line = aline
    return line
Run Code Online (Sandbox Code Playgroud)

所述num, ... in enumerate(..., 2)迭代器产生的序列2,3,4 ...的randrange因此将0的概率为1.0/num-这与我们必须替换当前选定的行(被引用的算法的样本大小1的特殊情况的概率- 请参阅Knuth的书中的正确性证明==当然我们也是在一个足够小的"水库"以适应记忆的情况下; - ))......以及我们这样做的概率.

  • 我一直认为`random.choice()`函数应该对任意迭代器和序列起作用,完全实现上述算法. (7认同)
  • @Greg Hewgill,那会很好,但每个第十个问题就是"我的迭代器去哪儿了" (3认同)
  • @aaron,对-相同的原因,例如,迭代器没有`len'...“算法”不难发现,但是使用迭代器被认为是一种经常令人惊讶的效果。当然,这是一系列艰难的设计决策(例如,sum_does_消耗了迭代器-决策是,求和很可能是用户所需的全部,而长度或一个随机项的可能性较小。 ..总是以任一种方式来做出错误的决定-如果我们有办法将名称清楚地标记为“具有副作用”,例如Ruby的尾声,设计选择可能会有所不同。 (2认同)

Ton*_*nen 29

import random
lines = open('file.txt').read().splitlines()
myline =random.choice(lines)
print(myline)
Run Code Online (Sandbox Code Playgroud)

对于非常长的文件:根据文件的长度在文件中寻找随机位置,并在位置(或换行符和文件结尾)后找到两个换行符.如果原始搜索位置<100,如果我们在最后一行内部结束,则在文件开头之前或之后再做100个字符.

然而,这是复杂的,因为文件是iterator.So使它列表并采取random.choice(如果你需要很多,使用random.sample):

import random
print(random.choice(list(open('file.txt'))))
Run Code Online (Sandbox Code Playgroud)

  • 如果任务只是读取一行,则将完整文件加载到内存中是没有意义的. (13认同)
  • 这是一个有效的解决方案,但它不会删除 \r\n 或 EOL。您需要添加 .rstrip() 来清理它 (2认同)

Nic*_*lfi 10

虽然我迟到了四年,但我认为我有最快的解决方案.最近我写了一个名为linereader的python包,它允许你操作文件句柄的指针.

以下是使用此包获取随机行的简单解决方案:

from random import randint
from linereader import dopen

length = #lines in file
filename = #directory of file

file = dopen(filename)
random_line = file.getline(randint(1, length))
Run Code Online (Sandbox Code Playgroud)

第一次这样做是最糟糕的,因为linereader必须以特殊格式编译输出文件.完成此操作后,无论文件大小如何,linereader都可以快速访问文件中的任何行.

如果您的文件非常小(小到足以容纳MB),那么您可以替换dopencopen,并且它在内存中创建文件的缓存条目.这不仅更快,而且在文件加载到内存时获得文件内的行数; 它是为你完成的.您需要做的就是生成随机行号.这是一些示例代码.

from random import randint
from linereader import copen

file = copen(filename)
lines = file.count('\n')
random_line = file.getline(randint(1, lines))
Run Code Online (Sandbox Code Playgroud)

我真的很开心,因为我看到有人可以从我的包装中受益!对不起,对于死的答案,但该包肯定可以应用于许多其他问题.


cji*_*cji 9

这取决于"太多"开销是什么意思.如果可以将整个文件存储在内存中,那么就像

import random

random_lines = random.choice(open("file").readlines())
Run Code Online (Sandbox Code Playgroud)

会做的伎俩.


Iva*_*dov 7

如果您不想使用f.read()或将整个文件加载到 RAM 中f.readlines(),您可以通过以下方式获取随机行:

import os
import random


def get_random_line(filepath: str) -> str:
    file_size = os.path.getsize(filepath)
    with open(filepath, 'rb') as f:
        while True:
            pos = random.randint(0, file_size)
            if not pos:  # the first line is chosen
                return f.readline().decode()  # return str
            f.seek(pos)  # seek to random position
            f.readline()  # skip possibly incomplete line
            line = f.readline()  # read next (full) line
            if line:
                return line.decode()  
            # else: line is empty -> EOF -> try another position in next iteration

Run Code Online (Sandbox Code Playgroud)

PS:是的,这是 Ignacio Vazquez-Abrams 在上面的答案中提出的,但是 a)他的答案中没有代码,b)我自己想出了这个实现;它可以返回第一行或最后一行。希望它对某人有用。

但是,如果您关心分发,那么此代码不适合您。


Eug*_*ash 5

Alex Martelli 的答案的稍微改进版本,它处理空文件(通过返回一个default值):

from random import randrange

def random_line(afile, default=None):
    line = default
    for i, aline in enumerate(afile, start=1):
        if randrange(i) == 0:  # random int [0..i)
            line = aline
    return line
Run Code Online (Sandbox Code Playgroud)

这种方法可用于使用O(n)时间和O(1)空间从任何迭代器中获取随机项。