有没有内置的方法可以做到这一点?如果没有,我该如何在不造成太多额外开销的情况下完成?
有没有内置的方法可以做到这一点?如果没有,我该如何在不造成太多额外开销的情况下完成?
虽然没有内置函数,但是可以使用算法R(3.4.2)
(Waterman的“Reservoir Algorithm”),该算法被收录在Knuth的著作“计算机程序设计艺术”中,并且这个算法还经过了大量的简化处理。
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
num, ... in enumerate(..., 2)
迭代器会产生序列 2,3,4...。因此,randrange
函数的返回值为0的概率是 1.0/num
-- 而我们必须用这个概率来替换当前选定的行(参考算法样本大小为1的特殊情况 -- 参见 Knuth 的书籍以证明正确性 == 当然,我们也处于足够小的“水库”情况,能够适应内存;-))...并且正是我们执行此操作的概率。random.choice()
函数应该能够在任意迭代器上运行,而不仅仅是序列,并实现与上述算法完全相同的功能。 - Greg Hewgilllen
...算法并不难看出,但是消耗迭代器被认为是一个太容易令人惊讶的效果。当然,这是一系列艰难的设计决策(例如,sum
确实会消耗迭代器——决策在于求和可能是用户所需的全部,而长度或一个随机项则不太可能如此...总是两难的决策——如果我们有一种明确标记名称为“具有副作用”的方法,就像Ruby的尾随感叹号一样,设计选择可能会有所不同)。 - Alex Martelliline = next(afile)
抛出 StopIteration
异常。 - Ali Touimport random
lines = open('file.txt').read().splitlines()
myline =random.choice(lines)
print(myline)
针对非常长的文件:
根据文件长度随机定位到文件的某个位置,并在该位置之后找到两个换行符(或者是一个换行符和文件结尾)。如果原始查找位置小于100并且我们最终在最后一行内,则从文件开头或末尾开始再次查找100个字符。
然而,这样做过于复杂,因为文件可以迭代。所以将其转化为列表,然后使用 random.choice(如果需要多个,请使用random.sample )。
import random
print(random.choice(list(open('file.txt'))))
这取决于您对“过多”的开销有何理解。如果可以将整个文件存储在内存中,那么可以考虑以下代码:
import random
random_lines = random.choice(open("file").readlines())
用这种方法可以解决问题。
虽然我晚了四年,但是我认为我有最快的解决方案。最近我写了一个名为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))
第一次执行这个操作是最糟糕的,因为linereader必须以特殊格式编译输出文件。完成后,无论文件大小如何,linereader都可以快速访问文件中的任何行。
如果您的文件非常小(足够小,适合MB),则可以将dopen
替换为copen
,并在内存中创建文件的缓存条目。这不仅更快,而且在加载到内存中时,您可以获得文件中的行数;它已经为您完成了。您只需要生成随机行号即可。以下是一些示例代码。
from random import randint
from linereader import copen
file = copen(filename)
lines = file.count('\n')
random_line = file.getline(randint(1, lines))
我刚看到有人可以从我的包中受益,感到非常高兴!抱歉没有及时回复,但这个包肯定可以用于许多其他问题。
f.read()
或者 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
补充:是的,Ignacio Vazquez-Abrams在他上面的答案中提出了这个方法,但a)他的答案中没有代码,b)我自己想到了这个实现方式;它可以返回第一行或最后一行。希望对某人有用。
然而,如果你关心分发问题,这段代码对你不是一个选择。
readline
函数。def read_random_line(f, chunk_size=16):
import os
import random
with open(f, 'rb') as f_handle:
f_handle.seek(0, os.SEEK_END)
size = f_handle.tell()
i = random.randint(0, size)
while True:
i -= chunk_size
if i < 0:
chunk_size += i
i = 0
f_handle.seek(i, os.SEEK_SET)
chunk = f_handle.read(chunk_size)
i_newline = chunk.rfind(b'\n')
if i_newline != -1:
i += i_newline + 1
break
if i == 0:
break
f_handle.seek(i, os.SEEK_SET)
return f_handle.readline()
import random
with open("file.txt", "r") as f:
lines = f.readlines()
print (random.choice(lines))
import random
choicefile=open("yourfile.txt","r")
linelist=[]
for line in choicefile:
linelist.append(line)
choice=random.choice(linelist)
print(choice)
linelist.remove(choice)
随机定位到一个位置,读取一行并将其丢弃,然后再读取另一行。行的分布可能不是正常的,但这并不总是重要的。