Ruby,我该如何设计解析器?

时间:2009-07-15 10:26:03

标签: ruby multithreading

我正在为Google编写一个小解析器,我不确定设计它的最佳方法是什么。主要问题是它将记住它停止的位置的方式。

在解析期间,它会将新搜索附加到文件的末尾,并使用第一行浏览文件startig。现在我想这样做,如果由于某种原因执行被中断,脚本知道它已成功完成的最后一次搜索。

一种方法是在获取文件后删除文件中的一行,但在这种情况下,我必须处理线程访问文件和删除文件中第一行的顺序无法有效处理。

另一种方法是将使用过的行数写入文本文件,并跳过编号在该文件中的行。或者我可能应该使用一些数据库? TIA

1 个答案:

答案 0 :(得分:0)

使用状态文件没有任何问题。唯一的问题是,在程序进入可能被中断的部分之前,您需要确保已完全提交对状态文件的更改。通常,这是通过IO#flush调用完成的。

例如,这是一个简单的状态跟踪类,它逐行工作:

class ProgressTracker
  def initialize(filename)
    @filename = filename
    @file = open(@filename)

    @state_filename = File.expand_path(".#{File.basename(@filename)}.position", File.dirname(@filename))

    if (File.exist?(@state_filename))
      @state_file = open(@state_filename, File::RDWR)
      resume!
    else
      @state_file = open(@state_filename, File::RDWR | File::CREAT)
    end
  end

  def each_line
    @file.each_line do |line|
      mark_position!
      yield(line) if (block_given?)
    end
  end

protected
  def mark_position!
    @state_file.rewind
    @state_file.puts(@file.pos)
    @state_file.flush
  end

  def resume!
    if (position = @state_file.readline)
      @file.seek(position.to_i)
    end
  end
end

您可以将其与类似IO的块调用一起使用:

test = ProgressTracker.new(__FILE__)

n = 0

test.each_line do |line|
  n += 1

  puts "%3d %s" % [ n, line ]

  if (n == 10)
    raise 'terminate'
  end
end

在这种情况下,程序会自行读取并在由于模拟错误而在十行之后停止。在第二次运行时,它应该显示接下来的十行,如果有那么多,或者只是在没有要检索的其他数据的情况下退出。

需要注意的是,如果要重新处理文件,或者文件已重置,则需要删除与输入数据关联的.position文件。也无法编辑文件并删除较早的行,否则会丢失偏移量跟踪。只要您只是将数据附加到文件或重新启动它,一切都会没问题。