积极前瞻和非捕获组差异

时间:2018-04-26 00:51:06

标签: ruby regex

当您想要匹配两种模式中的任何一种但未捕获它时,您将使用非捕获组?:

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

但是,如果我想捕获' _1'在字符串' john_1'中。它可能是' 2'或' '其次是其他任何事情。首先,我尝试了一个非捕获组:

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

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

相反,以下工作:

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

我用了一个积极的向前看。我发现积极前瞻的定义如下:

  

q(?= u)匹配q   接下来是你,没有让你成为比赛的一部分。积极的   lookahead构造是一对括号,带有开头   括号后跟一个问号和一个等号。

但是这个定义并不适合我的榜样。是什么让积极前瞻工作,但不是非捕获小组在我提供的例子中工作?

3 个答案:

答案 0 :(得分:5)

捕捉和匹配是两回事。 (?: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 :(得分: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"

答案 2 :(得分:1)

让我们考虑几种情况。

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

str = "john_1"

你有两个选择。

使用正面的背后隐藏

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

积极的外观要求“john”必须紧跟在下划线之前,但它不是返回的匹配的一部分。

使用捕获组:

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

此正则表达式与"john_1"匹配,但在捕获组1中捕获"_.+"。通过检查方法String#[]的文档,您将看到该方法的一种形式为{{ 1}},返回捕获组str[regexp, capture]的内容。此处capture等于capture,表示第一个捕获组。

请注意,下划线后面的字符串可能包含下划线:1

如果下划线可以在字符串的末尾,则在上面的正则表达式中将"john_1_a"[/(?<=john)_.+/] #=> "_1_a"替换为+(意味着在下划线后面匹配零个或多个字符)。

下划线前面的字符串可以是任意内容,而下划线后跟一个或多个字符

*

我们可能会考虑两种情况。

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

开头

在这种情况下,我们可以写:

str = "john_mary_tom_julie"

这是有效的,因为正则表达式默认为 greedy ,这意味着它将从遇到的第一个下划线开始。

返回的字符串以最后一个下划线开头

我们可以写一下:

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

此正则表达式匹配下划线,后跟一个或多个非下划线的字符,后跟字符串结束锚点(str[/_[^_]+\z/] #=> "_julie" )。

除此之外:方法\z

String#[]对于方法来说似乎是一个奇怪的名称,但它仍然是一种方法,所以它可以用传统的方式调用:

[]

表达式str.[](/john(_.+)/, 1) #=> "_1" syntactic sugar的一个例子(在Ruby中有很多)。编写str[/john(_.+)/, 1]时,Ruby会在评估之前将其转换为方法的常规表达式。