将正则表达式插入到另一个正则表达式中
在以下代码中, k2
与k1
最小不同。 也就是说,除了使用插值定义之外, 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
但是如下所示,使用带有k1
gsub
可以正常工作,而将其与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:...)
? 有没有办法按照我期望的方式完成这项工作?
我做的事情如下:
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"
使用字符串来保存这些字符并根据需要将其插入到正则表达式中。 Ruby试图用(?mix:)
覆盖一些基础,但它并没有预料到正则表达式会进入另一个正则表达式中的字符集。
背景资料
这是真正发生的事情:
在许多情况下,如果将正则表达式插入到正则表达式中,则有意义。 像这样
a = /abc/ #/abc/ b = /#{a}#{a}/ #/(?-mix:abc)(?-mix:abc)/ 'hhhhabcabchthth'.gsub(/abcabc/, '_') # "hhhh_hthth" 'hhhhabcabchthth'.gsub(b, '_') # "hhhh_hthth"
它按预期工作。 整个(?-mix:
thing是一种封装规则a
,以防万一b
有不同的标志(?-mix:
是区分大小写的,因为这是默认的。但是如果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
像往常一样匹配。
另外,为了避免搞乱捕获顺序, (?:
?:被添加进去创建一个未捕获的组。所有这些都是粗略尝试使a
和e
变量匹配你期望它们匹配的东西更大的正则表达式。
不幸的是,如果你把它放在字符集匹配中,意思是[]
,这个策略就完全失败了。 [(?-mix:)]
现在的解释完全不同了。 [^?-m]
表示不在“?”之间的所有内容 和“m”(包括),这意味着,例如,字母“c”不再出现在您的字符集中。 这意味着“c”不会像您在示例中看到的那样被替换为下划线。 你可以看到字母“x”发生同样的事情。 它也不会被下划线替换,因为它在否定字符集内,因此不在匹配的字符中。
Ruby没有费心去解析正则表达式来弄清楚你正在将正则表达式插入到一个字符集中,即使它确实如此,它仍然需要解析出v
变量以确定它也是一个字符集,因此你真正想要的是从v
的字符集中取出字符并将其与所有其他字符放在一起。
我的建议是,既然aeiouAEIOUäöüÄÖÜ
只是一堆字符,你可以将它存储在一个字符串中,并将其插入到正则表达式中的任何字符集中。 并且在将来将正则表达式插入正则表达式时要小心。 避免它,除非你真的确定它将要做什么。
我正在使用的答案:
如果要将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 )"
的用途。)
你的陈述“ k2
完全相同,只是它使用插值定义”是错误的。
当您插入非字符串的内容(例如regex v
,会将其转换为带有to_s
的字符串。
v = /[aeiouAEIOUäöüÄÖÜ]/ v.to_s # => "(?-mix:[aeiouAEIOUäöüÄÖÜ])"
这被内插到k2
,导致与k1
不同的正则表达式。 如果你想让k2
与k1
相同,你需要插入一个字符串:
v = "[aeiouAEIOUäöüÄÖÜ]"