我正在尝试从大文件(>百万行)中选择一个随机行,而不是选择任何重复项。如果有一个骗局,那么我想继续挑选,直到找到一个非欺骗。
到目前为止我得到了什么:
@already_picked = []
def random_line
chosen_line = nil
chosen_line_number = nil
File.foreach("OSPD4.txt").each_with_index do |line, number|
if rand < 1.0/(number+1)
chosen_line_number = number
chosen_line = line
end
end
chosen_line
if @already_picked.include(chosen_line_number)?
# what here?
else
@already_picked << chosen_line_number
end
end
100.times do |t|
random_line
end
我不确定在if
子句
答案 0 :(得分:2)
100万行不是很多。如果它们平均100字节/行,则内存为100MB。这么简单,继续前进
File.readlines("file").sample(100)
如果你开始说话大于容易适合内存,那么下一步是对文件进行一次传递以记录行位置,然后从中提取样本。
class RandomLine
def initialize(fn)
@file = File.open(fn,'r')
@positions = @file.lines.inject([0]) { |m,l| m << m.last + l.size }.shuffle
end
def pick
@file.seek(@positions.pop)
@file.gets
end
end
答案 1 :(得分:1)
每次请求随机行时,您的方法都可能会读取大量文件。更好的方法可能是读取整个文件一次并构建一个表格,其中每行开始(这样您就不必将所有数据保存在内存中)。假设文件没有改变,那么你可以在这个表中寻找一个随机位置并读取一行。快点。一种可能的实现方式:
class RandomLine
def initialize(filename)
@file = File.open(filename)
@table = [0]
@picked = []
File.foreach(filename) do |line|
@table << @table.last + line.size
end
end
def pick
return nil if @table.size == 0 # if no more lines, nil
i = rand(@table.size) # random line
@file.seek(@table[i]) # go to the line
@table.delete_at(i) # remove from the table
line = @file.readline
if @picked.include? line
pick # pick another line
else
@picked << line
line
end
end
end
用法:
random_line = RandomLine.new("OSPD4.txt")
100.times do
puts random_line.pick
end
答案 2 :(得分:1)
虽然为了避免将文件读入内存而花费大量工作是非常高尚的,但是数百万行并不是那么多。另一种方法是尝试一个简单的解决方案,只有在实践中实际上很慢时才会变得复杂。
class RandomLine
def initialize fn
open(fn, 'r') { |f| @i, @lines = -1, f.readlines.shuffle }
end
def pick
@lines[@i += 1]
end
end
q = o = RandomLine.new '/etc/hosts'
puts q while q = o.pick
答案 3 :(得分:1)
当读取文件返回行数组时,您可以使用#sample方法。
File.readlines("OSPD4.txt").sample(100).map{|line| line.chomp }
# using chomp to get rid of EOL