如何在另一个正则表达式中使用Regexp.union?

使用Ruby 2.4。 我想创建一个正则表达式,方法是匹配任意数量的空格,后跟我的数组中出现的字母。 所以我尝试了这个

LETTERS = ["a", "b"] # => ["a", "b"] data = ["asdf f", "sdfsdf x"] # => ["asdf f", "sdfsdf x"] data.grep(/(^|[[:space:]]+)[#{Regexp.union(LETTERS)}]$/i) # => ["asdf f", "sdfsdf x"] 

但正如你所看到的,尽管我的数组中的两个标记都没有结束,但两个标记都匹配。 我如何重写我的正则表达式来解释这个问题?

 Regexp.new("[[:space:]]+(#{Regexp.union(LETTERS).source})", Regexp::IGNORECASE) 

你可以使用这个正则表达式:

 LETTERS = ["a","b"] #=> ["a","b"] regex = Regexp.new("[[:space:]]+#{Regexp.union(LETTERS)}", Regexp::IGNORECASE) #=> /[[:space:]]+(?-mix:a|b)/i data = ["asdf f", "sdfsdf x"] #=> ["asdf f", "sdfsdf x"] data.grep(regex) #=> [] data = ["asdf f", "sdfsdf a"] #=> ["asdf f", "sdfsdf a"] data.grep(regex) #=> ["sdfsdf a"] 

但最内层的正则表达式不会忽略大小写。 感谢@ EricDuminil的解决方案,很容易看出错误。

如果您对Regexen和插值不是很谨慎,则会出现细微的错误。

你需要 :

 /[[:space:]]+(?:#{Regexp.union(LETTERS).source})$/i 

这是一个例子:

 LETTERS = %w(ab).freeze data = ['asdf f', 'sdfsdf x', 'test A', 'test a', 'testB', 'testb'] r = /[[:space:]]+(?:#{Regexp.union(LETTERS).source})$/i # /[[:space:]]+(?:a|b)$/i data.grep(r) # ["test A", "test a"] 

错误1

如果省略Regexp#source

 r2 = /[[:space:]]+(?:#{Regexp.union(LETTERS)})$/i # /[[:space:]]+(?:(?-mix:a|b))$/i data.grep(r2) # ["test a"] 

请注意, Regexp.union区分大小写。 当它被导入到更大的正则表达式时,它的标志也被导入: (a|b)区分大小写,因此它与"test A"不匹配。 这是一个相关的主题: 将正则表达式插入到另一个正则表达式中

错误2

如果省略a|b周围的parens:

 r3 = /[[:space:]]+#{Regexp.union(LETTERS).source}$/i # /[[:space:]]+a|b$/i data.grep(r3) # ["test A", "test a", "testB", "testb"] 

空格只会在a之前考虑。 "testB"即使不应该匹配。