当您想要匹配两种模式中的任何一种但未捕获它时,您将使用非捕获组?:
:
/(?:https?|ftp)://(.+)/
但是,如果我想捕获' _1'在字符串' john_1'中。它可能是' 2'或' '其次是其他任何事情。首先,我尝试了一个非捕获组:
'john_1'.gsub(/(?:.+)(_.+)/, "")
=> ""
它不起作用。我告诉它不要捕获一个或多个字符,而是捕获_及其后的所有字符。
相反,以下工作:
'john_1'.gsub(/(?=.+)(_.+)/, "")
=> "john"
我用了一个积极的向前看。我发现积极前瞻的定义如下:
q(?= u)匹配q 接下来是你,没有让你成为比赛的一部分。积极的 lookahead构造是一对括号,带有开头 括号后跟一个问号和一个等号。
但是这个定义并不适合我的榜样。是什么让积极前瞻工作,但不是非捕获小组在我提供的例子中工作?
答案 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">
编辑:我还应该提到sub
和gsub
对整个捕获工作,而不是单个捕获组(尽管可以在替换中使用)。
'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会在评估之前将其转换为方法的常规表达式。