【问题标题】:Interpolating regexes into another regex将正则表达式插入另一个正则表达式
【发布时间】:2013-05-23 02:28:58
【问题描述】:

在下面的代码中,k2k1 的区别很小。也就是说,k2 完全相同,只是它是使用插值定义的。 (也就是说,我期望完全一样;从p k2的结果显然不是。)

v  = /[aeiouAEIOUäöüÄÖÜ]/                 # vowels
k1 = /[[ßb-zB-Z]&&[^[aeiouAEIOUäöüÄÖÜ]]]/ # consonants defined without interpolation
k2 = /[[ßb-zB-Z]&&[^#{v}]]/               # consonants defined same way, but with interpolation

但如下所示,将gsubk1 一起使用是可行的,而将其与k2 一起使用会以我不理解的方式失败。

all_chars = "äöüÄÖÜß"<<('a'..'z').to_a.join<<('A'..'Z').to_a.join

p all_chars                  # "äöüÄÖÜßabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
p all_chars.gsub( k1 , '_' ) # "äöüÄÖÜ_a___e___i_____o_____u_____A___E___I_____O_____U_____"
p all_chars.gsub( k2 , '_' ) # "äöüÄÖÜ_abcdefghijklm_o_____u__x__ABCDEFGHIJKLMNOPQRSTUVWXYZ"
p k1                         # /[[ßb-zB-Z]&&[^[aeiouAEIOUäöüÄÖÜ]]]/
p k2                         # /[[ßb-zB-Z]&&[^(?-mix:[aeiouAEIOUäöüÄÖÜ])]]/

为什么它不起作用? (?-mix:...) 是什么?有没有办法让这项工作按我预期的方式工作?

【问题讨论】:

    标签: ruby regex string-interpolation


    【解决方案1】:

    我会做这样的事情:

    keywords = %w[foo bar]
    regex = /\b(?:#{ Regexp.union(keywords).source })\b/i
    # => /\b(?:foo|bar)\b/i
    

    当您想一次测试单个字符串中是否出现多个子字符串时,这很有用。

    将正则表达式插入字符串不一定能正常工作。默认情况下,当你这样做时,Ruby 使用to_s 转换模式,这不是我想要的,因为我不想要模式、标志和所有内容的完整字符串表示。使用 source 返回我想要的:

    regex = Regexp.union(keywords)
    regex         # => /foo|bar/
    regex.inspect # => "/foo|bar/"
    regex.to_s    # => "(?-mix:foo|bar)"
    regex.source  # => "foo|bar"
    

    【讨论】:

    【解决方案2】:

    使用字符串来保存这些字符并根据需要将其插入到正则表达式中。 Ruby 正在尝试使用 (?mix:) 覆盖一些基础,但它没有预料到正则表达式会进入另一个正则表达式中的字符集。

    背景信息

    这是真正发生的事情:

    在许多情况下,如果您将正则表达式插入到正则表达式中,这是有道理的。像这样

    a = /abc/       #/abc/
    b = /#{a}#{a}/  #/(?-mix:abc)(?-mix:abc)/
    
    'hhhhabcabchthth'.gsub(/abcabc/, '_')   # "hhhh_hthth"
    'hhhhabcabchthth'.gsub(b, '_')          # "hhhh_hthth"
    

    它按预期工作。整个(?-mix: 是封装a 规则的一种方式,以防b 有不同的标志。 a 区分大小写,因为这是默认设置。但是如果b 设置为不区分大小写,则a 继续匹配之前匹配的唯一方法是使用-i 确保它区分大小写。冒号后(?-i:) 内的任何内容都将区分大小写。以下内容更清楚地说明了这一点

    e = /a/i # e is made to be case insensitive with the /i
    /#{e}/   # /(?i-mx:a)/
    

    您可以在上面看到,当将e 插入到某个东西中时,您现在有了(?i-mx:)。现在i 位于- 的左侧,这意味着它会(暂时)打开而不是关闭不区分大小写,以便e 像往常一样匹配。

    另外,为了避免打乱捕获顺序,添加了(?: 来组成一个未捕获的组。所有这些都是粗略的尝试,使 ae 变量与您在将它们放入更大的正则表达式时所期望的匹配。

    不幸的是,如果你把它放在一个字符集匹配里面,意思是[],这个策略就完全失败了。 [(?-mix:)] 现在的解释完全不同。 [^?-m] 表示不在“?”之间的所有内容和“m”(包括),这意味着,例如,字母“c”不再在您的字符集中。这意味着正如您在示例中看到的那样,“c”不会被下划线替换。你可以看到字母“x”发生了同样的事情。它也不会被下划线替换,因为它在否定字符集中,因此不在被匹配的字符中。

    Ruby 不会费心去解析正则表达式来确定您正在将正则表达式插入到字符集中,即使这样做了,它仍然需要解析出 v 变量才能弄清楚它也是一个字符集,因此您真正想要的只是从v 中的字符集中获取字符并将它们与所有其他字符一起放在那里。

    我的建议是,既然aeiouAEIOUäöüÄÖÜ 只是一堆字符,你可以将它存储在一个字符串中,并将其插入到正则表达式中的任何字符集中。并且在将来将正则表达式插入正则表达式时要小心。除非你真的确定它会做什么,否则避免它。

    【讨论】:

      【解决方案3】:

      您的陈述“k2 完全一样,只是它是使用插值定义的”是错误的。

      当您插入不是字符串的内容时,例如正则表达式v,它会被转换为带有to_s 的字符串。

      v = /[aeiouAEIOUäöüÄÖÜ]/
      v.to_s # => "(?-mix:[aeiouAEIOUäöüÄÖÜ])"
      

      这被插入到k2,导致与k1 不同的正则表达式。如果你想让k2k1一样,你需要插入一个字符串:

      v = "[aeiouAEIOUäöüÄÖÜ]"
      

      【讨论】:

      • “您的说法“k2 完全一样,只是它是使用插值定义的”是错误的。”是的,我可以从p k2 看到这一点。我的意思是它打算是一样的......
      • 无论如何,这行得通:k3 = /[[ßb-zB-Z]&amp;&amp;[^#{v.inspect[1...-1]}]]/
      【解决方案4】:

      我正在使用的答案:

      如果您想将some_regex 插入另一个,请在#{} 中使用regex1.inspect[1...-1]

      例如,以我最初的例子为例,这种使用插值定义辅音的方法是可行的。

      v  = /[aeiouAEIOUäöüÄÖÜ]/                   # vowels
      k3 = /[[ßb-zB-Z]&&[^#{v.inspect[1...-1]}]]/ # consonants
      

      (我不知道是否有某种内置方法可以实现与 .inspect[1...-1] 相同的功能。

      我很惊讶.to_s 还不是正则表达式的工作方式。

      我仍然不确定"(?-mix:some_regex)" 的用途。)

      【讨论】:

      • 不要使用inspect,使用source。看我的回答。
      • 我建议阅读stackoverflow.com/questions/43057658/…,因为它有助于解决这些“我不知道”、“我很惊讶”和“我仍然不确定”修饰语的日志。
      猜你喜欢
      • 1970-01-01
      • 2013-07-13
      • 1970-01-01
      • 2012-04-10
      • 2015-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-07
      相关资源
      最近更新 更多