我正在尝试创建一个基本的ruby scraper,它将从html源代码中获取8个字母或更长的单词。然后将它们保存在与单词的第一个字符对应的文件中。好像很简单?
re = /\w{8,}/
cre = /[a-z0-9]/
a = b.html #This grabs the html from the browser
matchx = a.scan(re)
matchx.each do |xx|
word = xx.to_s.downcase.chomp
fchar = word[0].chr
if (fchar.match(cre)) #Not sure if I need this
@pcount += 1
fname = @WordsFName+fchar #@WordsFName is a prefix
tmpF = File.open(fname,"a+")
#Check for duplicates, if not write to file
exists = File.readlines(fname).any? { |li| li[word] }
if (!exists)
tmpF.write(word+"\n")
print word
@wcount += 1
end
end
end
Ruby成功抓取所有单词,获取第一个字符,并打开所有必需的文件,但无法写入。此外,打印方法打印所有单词,包括重复,但检查任何? irb的方法没有问题。
答案 0 :(得分:13)
File#write是缓冲的,你不会在你的write和File.readlines(fname)之间刷新或关闭tmpF,所以readlines在刷新之前永远不会看到输出。我没有看到任何关闭tmpF的调用,所以,当文件对象完成时,除了程序退出,或者在tmpF超出范围之后的某个时间点,除了程序退出之外,还不清楚何时刷新写入数据。
您可以在使用tmpF.flush
进行写入后手动刷新,或者在打开后使用tmpF.sync = true
进行默认行为。
请注意,随着每个文件变大,重复检查的成本会在重新读取整个文件时成为气球。如果单词集适合内存,可以考虑保留一个你看过的单词的散列,如果它比存储在内存中的大,那么考虑一个键值存储,而不是每次重读一个串行文件。
我在irb中玩弄了解冲洗行为。 OP代码的主要问题是tmpF文件没有显式/隐式刷新或关闭。因此,只有当tmpF File对象被垃圾收集或程序退出时才会写入可能小于缓冲区大小的部分写入。每次循环时,tmpF都会被分配一个新打开的文件对象,因此在之前的迭代中打开的文件只有在GC完成后才会被刷新。
irb(main):001:0> t=File.open('zzz','a+')
=> #<File:zzz>
irb(main):002:0> t.write '123'
=> 3
irb(main):003:0> File.readlines('zzz')
=> []
irb(main):004:0> t=File.open('zzz','a+')
=> #<File:zzz>
irb(main):005:0> t.write '456'
=> 3
irb(main):006:0> File.readlines('zzz')
=> []
irb(main):007:0> t.close
=> nil
irb(main):008:0> File.readlines('zzz')
=> ["456"]
irb(main):009:0> t=File.open('zzz','a+')
=> #<File:zzz>
irb(main):010:0> t.write '789'
=> 3
irb(main):011:0> File.readlines('zzz')
=> ["456"]
irb(main):012:0> t.flush
=> #<File:zzz>
irb(main):013:0> File.readlines('zzz')
=> ["456789"]
irb(main):014:0> GC.start
=> nil
irb(main):015:0> File.readlines('zzz')
=> ["456789123"]