ruby - 基于匹配的回调正则表达式

标签 ruby regex callback finite-automata

我有一个函数,它接受一个输入字符串,然后通过几个正则表达式运行该字符串,直到找到匹配项。找到匹配项后,我将返回一个输出,该输出是原始字符串和匹配项的函数。所以在 ruby 中:

str = "my very long original string ... millions of characters"
case str
  when regex1
    do_something1(str,$1)
  when regex2
    do_something2(str,$1)
  when regex3
    do_something3(str,$1)
  ...
  when regex100000
    do_something100000(str,$1)
  else
    do_something_else(str)
end

现在,Ruby 是否真的优化了这个 switch 循环,以便 str 只被遍历一次?假设不是,则可以使用一个具有嵌入式回调的大而长的正则表达式更有效地执行此功能。像这样:

/(?<callback:do_something1>regex1)|
(?<callback:do_something2>regex2)|
(?<callback:do_something3>regex3)|
   ...
(?<callback:do_something100000>regex100000)/

有什么技术可以做到这一点吗?

最佳答案

如果是 Ruby 1.9,并且如果您在正则表达式中使用命名组,那么您可以使用一些技巧将所有正则表达式组合成一个。这是执行繁重工作的类:

class BigPatternMatcher

  def initialize(patterns_and_functions, no_match_function)
    @regex = make_big_regex(patterns_and_functions)
    @no_match_function = no_match_function
  end

  def match(s, context)
    match = @regex.match(s)
    context.send(function_name(match), match)
  end

  private

  FUNC_GROUP_PREFIX = "func_"
  FUNC_GROUP_REGEX = /^#{FUNC_GROUP_PREFIX}(.*)$/

  def function_name(match)
    if match
      match.names.grep(FUNC_GROUP_REGEX).find do |name|
        match[name]
      end[FUNC_GROUP_REGEX, 1]
    else
      @no_match_function
    end
  end

  def make_big_regex(patterns_and_functions)
    patterns = patterns_and_functions.map do |pattern, function|
      /(?<#{FUNC_GROUP_PREFIX}#{function}>#{pattern.source})/
    end
    Regexp.union(patterns)
  end

end

我们会回过头来看看它是如何工作的。要使用它,您需要一个正则表达式列表和应为每个正则表达式调用的函数的名称。确保只使用命名组:

PATTERNS_AND_FUNCTIONS = [
  [/ABC(?<value>\d+)/, :foo],
  [/DEF(?<value>\d+)/, :bar],
]

还有函数,包括在没有匹配项时调用的函数:

def foo(match)
  p ["foo", match[:value]]
end

def bar(match)
  p ["bar", match[:value]]
end

def default(match)
  p ["default"]
end

最后,这是它的使用方式。 BigPatternMatcher#match 接受要匹配的字符串,以及调用该函数的对象:

matcher = BigPatternMatcher.new(PATTERNS_AND_FUNCTIONS, :default)
matcher.match('blah ABC1 blah', self)    # => ["foo", "1"
matcher.match('blah DEF2 blah', self)    # => ["bar", "2"]
matcher.match('blah blah', self)         # => ["default"]

请参阅下面的折叠部分,了解让它发挥作用的技巧。


BigPatternMatcher#make_big_regex 将所有正则表达式合并为一个,每个正则表达式都包含在括号中并由 | 分隔,例如

/(ABC(?<value>\\d+))|(DEF(?<value>\\d+))/

但这还不够。当其中一个子表达式匹配时,我们需要一些方法来识别匹配的子表达式,以及调用哪个函数。为此,我们将使每个子表达式成为其自己的命名组,名称基于应调用的函数:

/(?<func_foo>ABC(?<value>\\d+))|(?<func_bar>DEF(?<value>\\d+))/

现在,让我们看看如何从匹配到调用函数。给定字符串:

"blah ABC1 blah"

那么匹配组是:

#<MatchData "ABC1" func_foo:"ABC1" value:"1" func_bar:nil value:nil>

要确定调用哪个函数,我们只需找到名称以“func_”开头且具有非零值的匹配项。组名中“func_”之后的部分命名要调用的函数。


根据您问题中的直接技术来衡量此方法的性能是对读者的练习。

关于ruby - 基于匹配的回调正则表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11333574/

相关文章:

regex - LibXML中的xPath可以是正则表达式类型吗

javascript - 未使用回调时函数返回未定义,使用回调时,JS 说函数未定义

html - 更改 chrome 和 firefox 的 file_field_tag 文本字段大小

c - Ruby 的专有 C 扩展

ruby-on-rails - 可以将时间戳存储为哈希中的键并使其可排序吗?

ruby-on-rails - 有没有办法将 after_commit 回调设置为特定的 ActiveRecord 事务

javascript - 如何删除不必要的括号?

html - 如何使用正则表达式来匹配HTML中的charset字符串?

python - 从 TKinter 小部件检索/取回命令回调函数

ruby-on-rails - 模型中的 Ruby on Rails 增量计数器