【问题标题】:Variable containing regex as string/generating regex dynamically and trouble with \b包含正则表达式作为字符串的变量/动态生成正则表达式和 \b 的问题
【发布时间】:2015-02-18 04:03:58
【问题描述】:

我有这些变量:

keywords = ["/(?=.*?\bTest1\b).*/i","/(?=.*?\bTest2\b)(?=.*?\bTest3\b).*(?m)^(?!.*?NotThis4)(?m)^(?!.*?NotThis5).*$/i"]

hash = {"Test2 Test3 irrelevant1"=>"Mon, 16 Feb 2015 09:26:02 +0000", "Test2 Test3 NotThis4 irrelevant2"=>"Mon, 16 Feb 2015 09:24:01 +0000", "Test1 irrelevant3 irrelevant4"=>"Mon, 16 Feb 2015 09:23:02 +0000"}

我需要跑步:

keywords.each do |regex|
  hash.select{ |k,_| k[regex]}
end

在此示例中,我尝试使用 "Test2 Test3 irrelevant1""Test1 irrelevant4 irrelevant5" 的键收集哈希值。不过,正则表达式不是我关心的问题。它使用正则表达式作为/在一个我无法理解的变量中。我尝试将\b 转义为\\b,但无济于事。

当我将变量设置为正则表达式时,如:

regex = "/(?=.*?\bTest2\b)(?=.*?\bTest3\b).*(?m)^(?!.*?NotThis4)(?m)^(?!.*?NotThis5).*$/i"

代码:

hash.select{ |k,_| k[regex]}

不工作。

但如果我用实际的文字表达式替换变量:

hash.select{ |k, _| k[/(?=.*?\bTest2\b)(?=.*?\bTest3\b).*(?m)^(?!.*?NotThis4)(?m)^(?!.*?NotThis5).*$/i]}

效果很好。

此外,该功能也适用于文字字符串变量:

regex = "Test1"
hash.select{ |k, _| k[regex]}

以及文字字符串本身:

hash.select{ |k, _| k["Test1"]}

如何在变量中使用正则表达式,功能位于顶部?再次强调:

keywords.each do |regex|
  hash.select{ |k,_| k[regex]}
end

正则表达式作为字符串接收:

keywords.map! do |array_lineitem|
        builder = ""
        last = ""
        array_lineitem.each do |string_element|
          if string_element[0] == "-"
                string_element.sub!(/^-/, '')
                last += "(?m)^(?!.*?" + string_element + ")"
            else 
                builder += "(?=.*?\b" + string_element + "\b)"  
            end
        end
        if last.empty?
            throwback = "/" + builder + ".*/i"  
        else 
            throwback = "/" + builder + ".*" + last + ".*$" + "/i"
        end
    end 

将字符串转换为正则表达式,我尝试了to_regexp gemRegexp.escapeRegexp.union 和 eval(string),但还是没有运气。 \b 使用这些方法中的每一个都转换为 \x08

【问题讨论】:

标签: ruby regex sinatra


【解决方案1】:

这并不难,但看起来你正在这样做:

foo = '\b[ab]'
Regexp.new(foo) # => /\b[ab]/
/#{foo}/ # => /\b[ab]/

或:

foo = "\\b[ab]"
Regexp.new(foo) # => /\b[ab]/
/#{foo}/ # => /\b[ab]/

Ruby 非常乐意使用字符串来创建模式,您只需正确操作即可。

字符串是模式的重要组成部分,因为我们可以从较小的部分构建模式,然后最终将我们想要的部分连接成一个大模式。我们也在各种语言中这样做,而不仅仅是 Ruby。

WORD_BOUNDARY = '\b'
WORD_CHARACTERS = '[a-zA-Z]'
WORD_PATTERN = /#{WORD_BOUNDARY}#{WORD_CHARACTERS}+#{WORD_BOUNDARY}/
WORD_PATTERN # => /\b[a-zA-Z]+\b/

/#{WORD_PATTERN}/ # => /(?-mix:\b[a-zA-Z]+\b)/
Regexp.new(WORD_PATTERN) # => /\b[a-zA-Z]+\b/

注意"\b"'\b' 之间的区别也很重要。如果字符串允许对变量和转义值进行插值,则\b 将被视为退格。这不是你想要的:

"\b" # => "\b"
"\b".ord # => 8

改为使用非解释字符串:

'\b' # => "\\b"

或双转义字边界字符。

您可以轻松地动态生成模式,您只需要遵循字符串插值的规则,并了解如果字符串被插值,转义字符必须进行双重转义。

【讨论】:

  • 双重转义 (\\b) 让我走得更远。谢谢。在此之后剩下的问题是完整的正则表达式,以/ 和结尾/i,在字符串中:foo = "/(?=.*?\\bTest\\b).*/i"Regexp.new(foo) # => /\/(?=.*?\bTest\b).*\/i/eval(foo)Regex.try_convert(foo) 都成功了,包括斜杠和选项。 eval(foo) # => /(?=.*?\bTest\b).*/iRegexp.try_convert(foo) #=> /(?=.*?\bTest\b).*/i
【解决方案2】:

您为什么认为它与\b 有任何关系?

当我将变量设置为正则表达式时,如:

   regex = "/(?=.*?\bTest2\b)(?=.*?\bTest3\b).*(?m)^(?!.*?NotThis4)(?m)^(?!.*?NotThis5).*$/i"

代码

hash.select{ |k,_| k[正则表达式]}

您尚未将变量设置为正则表达式。您已将变量设置为以/ 开头和结尾的字符串,并且其中包含正则表达式的定义,true。要将变量实际设置为正则表达式,不要使用定义字符串的双引号,而是像这样:

>        regex = /(?=.*?\bTest2\b)(?=.*?\bTest3\b).*(?m)^(?!.*?NotThis4)(?m)^(?!.*?NotThis5).*$/i

现在您已将变量设置为正则表达式,而不是包含正则表达式源代码的字符串。

根据您的描述,我认为这可能是您的问题。如果您的问题实际上是正则表达式本身的定义与您想要的不匹配——这种情况经常发生在复杂的正则表达式中——最好的调试方法是从一个更简单的正则表达式开始,确认它与你想要的匹配,然后逐步构建您的复杂正则表达式,确保在每一步它仍然符合您的期望。

可以通过插值动态生成正则表达式。正则表达式 // 文字支持使用 #{} 构造的字符串插值,与字符串文字相同。例如:

regex = /(?m)^(?!.*?#{string_element})/

如果您的 string_element 中包含特殊的正则表达式控制字符,您可能希望使用 Regex.escape,如果它旨在准确地表示其中的内容为文字:

regex = /(?m)^(?!.*?#{Regexp.escape string_element})/

如果你在字符串中有一个正则表达式定义,你可以用它创建一个正则表达式:

string = "some?(regex|or)something\Z"
regex  = Regexp.new(string)

puts string.class #=> String
puts regex.class #=> Regexp

我不确定你是否真的想在这里这样做,但你可以。我不得不承认我并不完全理解你想要做什么,并且我不相信你的方法是你实际总体目标的最佳方法。

但至于如何创建具有动态插值内容的正则表达式文字并将其保存在变量中,这不是问题,希望这会有所帮助。

【讨论】:

  • 相应地更新了标题,并对正则表达式作为字符串进行了编辑。
  • 好的,你现在明白了吗? a = "/foo/" 没有将变量设置为正则表达式。 a = /foo/ 是。我认为这仍然是你的答案。这有意义吗?
  • 我现在得到了"/foo/" != /foo/ 部分。谢谢。但是,问题是,我可以选择要么无法动态生成正则表达式,要么选择能够在字符串中动态生成正则表达式,但由于\b 转换为@ 的问题,因此无法将字符串转换为正则表达式987654334@。因此,尽管理解得更多,但我也同样陷入困境。
  • 我剩下的问题是字符串包含完整的正则表达式,包括开始和结束的斜杠以及选项。但是根据我对"regex" != /regex/ 的理解和双重转义,我确实设法专注于解决这个问题,以便从字符串动态转换完整的正则表达式。我会写一篇文章。
【解决方案3】:

使用Tin Man's 双转义字符串数组:

keywords = ["/(?=.*?\\bTest1\\b).*/i","/(?=.*?\\bTest2\\b)(?=.*?\\bTest3\\b).*(?m)^(?!.*?NotThis4)(?m)^(?!.*?NotThis5).*$/i"]

还有这个哈希:

hash = {"Test2 Test3 irrelevant1"=>"Mon, 16 Feb 2015 09:26:02 +0000", "Test2 Test3 NotThis4 irrelevant2"=>"Mon, 16 Feb 2015 09:24:01 +0000", "Test1 irrelevant3 irrelevant4"=>"Mon, 16 Feb 2015 09:23:02 +0000"}

我可以使用eval(foo) 将完整正则表达式定义的字符串版本转换为jrochkind's(非字符串)正则表达式。安装了 'to_regexp' gem Regexp.try_convert(foo)Regexp.union(foo)) 也可以使用。

keywords.map! do |string|
  eval(string) # or Regexp.try_convert(string) with the 'to_regexp' gem
end 

keywords.map do |regex|  
  hash.select{ |k, _| k[regex]}
end

要得到想要的结果:

# => [{"Test1 irrelevant3 irrelevant4"=>"Mon, 16 Feb 2015 09:23:02 +0000"}, {"Test2 Test3 irrelevant1"=>"Mon, 16 Feb 2015 09:26:02 +0000"}]

我的实际代码现在更新和结构如下:

keywords = [["Test1"], ["Test2", "Test3", "-NotThis4", "-NotThis5"]]

hash = {"Test2 Test3 irrelevant1"=>"Mon, 16 Feb 2015 09:26:02 +0000", "Test2 Test3 NotThis4 irrelevant2"=>"Mon, 16 Feb 2015 09:24:01 +0000", "Test1 irrelevant3 irrelevant4"=>"Mon, 16 Feb 2015 09:23:02 +0000"}

keywords.map! do |array_lineitem|
        builder = ""
        last = ""
        array_lineitem.each do |string_element|
          if string_element[0] == "-"
                string_element.sub!(/^-/, '')
                last += '(?m)^(?!.*?' + string_element + ')'
            else 
                builder += '(?=.*?\b' + string_element + '\b)'  
            end
        end
        if last.empty?
            throwback = "/" + builder + ".*/i"  
        else 
            throwback = "/" + builder + ".*" + last + ".*$" + "/i"
        end
        eval(throwback) # or Regexp.try_convert(throwback) with the 'to_regexp' gem
    end

# => [/(?=.*?\bTest1\b).*/i, /(?=.*?\bTest2\b)(?=.*?\bTest3\b).*(?m)^(?!.*?NotThis4)(?m)^(?!.*?NotThis5).*$/i]

keywords.map do |regex|  
        hash.select{ |k, _| k[regex]}
    end

# => [{"Test1 irrelevant3 irrelevant4"=>"Mon, 16 Feb 2015 09:23:02 +0000"}, {"Test2 Test3 irrelevant1"=>"Mon, 16 Feb 2015 09:26:02 +0000"}]

【讨论】:

    猜你喜欢
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 2015-09-06
    • 1970-01-01
    • 2021-10-04
    • 2020-07-13
    相关资源
    最近更新 更多