如何有效地判断正则表达式中的哪一个与给定字符串匹配?

时间:2016-12-22 08:03:13

标签: ruby regex optimization

我正在开发一个Ruby项目,用户可以在其中配置正则表达式列表,并将每个表达式与要执行的某个操作相关联。然后针对某个给定的输入字符串测试该表达式列表,并且将执行匹配表达式的处理程序。代码基本上如下所示:

@mapping = {
    /^[A-Za-z]+$/ => Proc.new { puts "Got word" },
    /^\d+$/ => Proc.new { puts "Got number" },
    /^.$/ => Proc.new { puts "Got single character" }
}

def run_handlers(s)
    @mapping.each { |regexp, handler| handler.call if regexp =~ s.to_s }
end

# Prints 'Got word'
run_handlers("StackOverflow")

# Prints 'Got number' as well as 'Got single character'
run_handlers(4)
唉,当有很多(几千个)处理程序时,这表现得相当糟糕。对所有处理程序执行线性搜索似乎有点浪费,但由于表达式是用户可配置的,因此我无法轻易地利用表达式的某些特定特征。我想知道是否有更有效的方法来做到这一点。

我正在考虑的一个想法是将多个正则表达式(即多个状态机)合并到一个能够更有效地运行的大型状态机中。我知道合并正则表达式的union方法,但由于我需要根据哪个表达式匹配执行一组不同的处理程序,这似乎没什么用。

我在想,也许表达式可以在内部像树结构一样进行管理(很像Trie,这样程序实际上不需要尝试所有正则表达式,但可以快速删除整个表达式集。

也许已经存在一些针对此类问题的现有代码?我想各种Web框架在获取请求时必须处理相同类型的问题,并且必须决定选择哪条路由。

1 个答案:

答案 0 :(得分:1)

您可以使用括号捕获正则表达式匹配的部分。看看这个:

['1','a','Z'].each {|x| p /((\d)|([a-z])|([A-Z]))/.match(x).to_a }

我通过其/((\d)|([a-z])|([A-Z]))/方法将三个字符串传递给正则表达式match。然后我将输出转换为数组,以便于比较。

它包含三个正则表达式元素:

(\d)    # any number
([a-z]) # lowercase letters
([A-Z]) # uppercase letters

请注意,每个都括在括号内。

然后将它们全部包含在an OR block(a|b|c)

输出:

["1", "1", "1", nil, nil]
["a", "a", nil, "a", nil]
["Z", "Z", nil, nil, "Z"]

第一个匹配是完整正则表达式的匹配,第二个匹配是外部OR块。

所以这是感兴趣的第三到第五场比赛。它们与内在元素相匹配。

我认为你可能遇到的主要问题是内部元素本身是否具有OR块和捕获块。例如,看看我的代码的这个变种:

['1','a','Z'].each {|x| p /((\d)|(([a-g])|([h-z]))|([A-Z]))/.match(x).to_a }

在此我将第二个元素分成两个(([a-g])|([h-z]))

从输出中可以看出,这会抛出结果:

["1", "1", "1", nil, nil, nil, nil]
["a", "a", nil, "a", "a", nil, nil]
["Z", "Z", nil, nil, nil, nil, "Z"]

因此,如果您无法控制每个正则表达式元素的结构,我就不会认为您能够预测输出的结构。