正向先行断言和非捕获组的区别

10

当你想匹配两种模式中的任意一种但不需要捕获它时,你可以使用非捕获组?:

/(?:https?|ftp)://(.+)/

但是如果我想从字符串'john_1'中捕获'_1',该怎么办呢?它可能是以'2'或''开头,后面跟着任何其他字符。 我首先尝试了一个非捕获组:

'john_1'.gsub(/(?:.+)(_.+)/, "")
=> ""

它不起作用。我告诉它不要捕获一个或多个字符,而是捕获_和其后的所有字符。

相反,以下内容可以正常工作:

'john_1'.gsub(/(?=.+)(_.+)/, "")
=> "john"

我使用了正向先行断言。我找到的正向先行断言的定义如下:

q(?=u)匹配后面跟着u的q,但不将u包含在匹配结果中。正向先行断言构造是一对括号,其中开头的括号后跟一个问号和等于号。

但是这个定义并不完全适用于我的例子。是什么使得正向先行断言能够起作用,而非捕获组无法在我提供的示例中起作用呢?

在这种情况下,'john_1'.split('_').first 不是更适合吗? - Aleksei Matiushkin
3个回答

27

捕获和匹配是两个不同的概念。(?:expr) 不会捕获 expr,但仍然包含在匹配的字符串中。零宽断言,例如 (?=expr),既不捕获也不包括 expr 在匹配的字符串中。

也许一些示例将有助于说明区别:

> "abcdef"[/abc(def)/] # => abcdef
> $1 # => def

> "abcdef"[/abc(?:def)/] # => abcdef
> $1 # => nil

> "abcdef"[/abc(?=def)/] # => abc
> $1 # => nil

当你在String#gsub中使用非捕获组时,它仍然是匹配的一部分,并且会被替换为替换字符串。


1
你的第一个示例无法正常工作,因为非捕获组仍然是整个捕获的一部分,而回顾断言仅用于匹配但不是整体捕获的一部分。
如果获取实际匹配数据,则更容易理解。
# Non-capturing group
/(?:.+)(_.+)/.match 'john_1'
=> #<MatchData "john_1" 1:"_1">

# Positive Lookbehind
/(?=.+)(_.+)/.match 'john_1'
=> #<MatchData "_1" 1:"_1">

编辑:我还应该提到,subgsub适用于整个捕获,而不是单个捕获组(尽管这些可以用于替换)。
'john_1'.gsub(/(?:.+)(_.+)/, 'phil\1')
=> "phil_1"

1

让我们考虑几种情况。

下划线前面的字符串必须是 "john",下划线后跟着一个或多个字符

str = "john_1"

你有两个选择。

使用正向先行断言

str[/(?<=john)_.+/]
  #=> "_1"

正向后行断言要求"john"必须立即出现在下划线之前,但它不是返回的匹配的一部分。
使用捕获组:
str[/john(_.+)/, 1]
  #=> "_1"

这个正则表达式匹配"john_1",但是"_.+"被捕获在第一组中。通过查看String#[]方法的文档,您将看到该方法的一种形式是str [regexp,capture],它返回捕获组capture的内容。这里capture等于1,表示第一个捕获组。
请注意,下划线后面的字符串可能包含下划线:"john_1_a"[/(?<=john)_.+/] #=> "_1_a"
如果下划线可以在字符串末尾,请在上述正则表达式中将+替换为*(表示在下划线后匹配零个或多个字符)。 下划线前面的字符串可以是任何内容,下划线后面跟着一个或多个字符
str = "john_mary_tom_julie"

我们可以考虑两种情况。
在这种情况下,我们可以编写以下代码:

返回的字符串应以第一个下划线开头

代码如下:

str[/_.+/]
  #=> "_mary_tom_julie"

这个方法有效是因为正则表达式默认是贪婪模式,意味着它会从第一个下划线开始匹配。
返回的字符串应该以最后一个下划线开头。
在这里我们可以写:
str[/_[^_]+\z/]
  #=> "_julie"

这个正则表达式匹配一个下划线,后面跟着一个或多个非下划线字符,然后是字符串结尾锚点 (\z)。 < p > 附注:方法 String#[]

< p >[] 可能看起来是一个奇怪的方法名,但它仍然是一个方法,因此可以按照常规方式调用:

str.[](/john(_.+)/, 1)
  #=> "_1"

这里的表达式str[/john(_.+)/, 1]是Ruby中许多语法糖例子之一。当写成str[...]时,Ruby会将其转换为常规方法表达式再进行评估。请注意,保留html标签。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接