Ruby参数化if ... then blocks

时间:2009-12-09 13:33:11

标签: ruby metaprogramming

我正在解析文本文件,并希望能够扩展可以轻松识别的令牌集。目前我有以下内容:

if line =~ /!DOCTYPE/ 
     puts "token doctype   " + line[0,20]   
     @ast[:doctype] << line
  elsif line =~ /<html/ 
     puts "token main HTML start   " + line[0,20]
     html_scanner_off = false
 elsif line =~ /<head/ and not html_scanner_off
     puts "token HTML header starts   " + line[0,20]
     html_header_scanner_on = true
  elsif line =~ /<title/ 
     puts "token HTML title   " + line[0,20]
     @ast[:HTML_header_title] << line 
  end

有没有办法用yield块写这个,例如类似的东西:

scanLine("title", :HTML_header_title, line)

2 个答案:

答案 0 :(得分:2)

如果您打算解析HTML内容,您可能希望使用其中一种非常高质量的HTML解析器,如nokogiri(http://nokogiri.org/)或Hpricot(http://hpricot.com/)。一个自己动手的方法可能需要更长的时间来完善,而不是弄清楚如何使用其中一个解析器。

另一方面,如果你正在处理的东西不是HTML,并且无法以这种方式解析,那么你需要以某种方式推销自己的东西。有一些Ruby解析器框架可能会有所帮助,但对于性能不是关键因素的简单任务,你可以像在这里一样使用一堆正则表达式。

答案 1 :(得分:2)

Don't parse HTML with regexes.

除此之外,有几种方法可以做你正在谈论的事情。之一:

class Parser
        class Token
                attr_reader :name, :pattern, :block
                def initialize(name, pattern, block)
                        @name = name
                        @pattern = pattern
                        @block = block
                end

                def process(line)
                        @block.call(self, line)
                end
        end

        def initialize
                @tokens = []
        end

        def scanLine(line)
                @tokens.find {|t| line =~ t.pattern}.process(line)
        end

        def addToken(name, pattern, &block)
                @tokens << Token.new(name, pattern, block)
        end
end

p = Parser.new
p.addToken("title", /<title/) {|token, line| puts "token #{token.name}: #{line}"}
p.scanLine('<title>This is the title</title>')

这有一些限制(比如不检查重复的令牌),但有效:

  

$ ruby​​ parser.rb
  标题标题:&lt; title&gt;这是标题&lt; / title&gt;
  $