如何在Ruby中的其他正则表达式中嵌入正则表达式

the*_*Man 7 ruby regex

我有一个字符串:

'A Foo'
Run Code Online (Sandbox Code Playgroud)

并希望在其中找到"Foo".

我有一个正则表达式:

/foo/
Run Code Online (Sandbox Code Playgroud)

我正在嵌入另一个不区分大小写的正则表达式,所以我可以按步骤构建模式:

foo_regex = /foo/
pattern = /A #{ foo_regex }/i
Run Code Online (Sandbox Code Playgroud)

但它不会正确匹配:

'A Foo' =~ pattern # => nil
Run Code Online (Sandbox Code Playgroud)

如果我将文本直接嵌入到模式中,它可以工作:

'A Foo' =~ /A foo/i # => 0
Run Code Online (Sandbox Code Playgroud)

怎么了?

the*_*Man 7

从表面上看,似乎将模式嵌入到另一个模式中只会起作用,但这是基于模式在Ruby中如何工作的错误假设,它们只是字符串.使用:

foo_regex = /foo/
Run Code Online (Sandbox Code Playgroud)

创建一个Regexp对象:

/foo/.class # => Regexp
Run Code Online (Sandbox Code Playgroud)

因此,它了解用于创建它的可选标志:

( /foo/    ).options # => 0
( /foo/i   ).options # => 1
( /foo/x   ).options # => 2
( /foo/ix  ).options # => 3
( /foo/m   ).options # => 4
( /foo/im  ).options # => 5
( /foo/mx  ).options # => 6
( /foo/imx ).options # => 7
Run Code Online (Sandbox Code Playgroud)

或者,如果你喜欢二进制:

'%04b' % ( /foo/    ).options # => "0000"
'%04b' % ( /foo/i   ).options # => "0001"
'%04b' % ( /foo/x   ).options # => "0010"
'%04b' % ( /foo/xi  ).options # => "0011"
'%04b' % ( /foo/m   ).options # => "0100"
'%04b' % ( /foo/mi  ).options # => "0101"
'%04b' % ( /foo/mx  ).options # => "0110"
'%04b' % ( /foo/mxi ).options # => "0111"
Run Code Online (Sandbox Code Playgroud)

并且无论何时使用Regexp,无论是作为独立模式还是嵌入到另一个模式中,都会记住这些.

如果我们在嵌入后查看模式是什么样的,您可以看到这一点:

/#{ /foo/  }/ # => /(?-mix:foo)/
/#{ /foo/i }/ # => /(?i-mx:foo)/
Run Code Online (Sandbox Code Playgroud)

?-mix:以及?i-mx:这些选项如何以嵌入模式表示.

根据选项的Regexp文档:

i,mx也可以在子表达式水平与施加的(?开关)构建体,其使得选项,并禁用选项关闭用于由括号包围的表达.

因此,Regexp记住了这些选项,即使在外部模式中也是如此,导致整体模式失败:

pattern = /A #{ foo_regex }/i # => /A (?-mix:foo)/i
'A Foo' =~ pattern # => nil
Run Code Online (Sandbox Code Playgroud)

可以确保所有子表达式与周围的模式匹配,但是这很快就会变得过于复杂或混乱:

foo_regex = /foo/i
pattern = /A #{ foo_regex }/i # => /A (?i-mx:foo)/i
'A Foo' =~ pattern # => 0
Run Code Online (Sandbox Code Playgroud)

相反,我们有source返回模式文本的方法:

/#{ /foo/.source  }/ # => /foo/
/#{ /foo/i.source }/ # => /foo/
Run Code Online (Sandbox Code Playgroud)

使用其他Regexp方法时,也会出现记住选项的嵌入式模式的问题,例如union:

/#{ Regexp.union(%w[a b]) }/ # => /(?-mix:a|b)/
Run Code Online (Sandbox Code Playgroud)

再次,source可以帮助:

/#{ Regexp.union(%w[a b]).source }/ # => /a|b/
Run Code Online (Sandbox Code Playgroud)

知道这一切:

foo_regex = /foo/
pattern = /#{ foo_regex.source }/i # => /foo/i
'A Foo' =~ pattern # => 2
Run Code Online (Sandbox Code Playgroud)