【问题标题】:Positive Lookahead and Non-capturing group difference正前瞻和非捕获组差异
【发布时间】:2018-04-26 00:51:06
【问题描述】:

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

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

但是如果我想在字符串 'john_1' 中捕获 '_1' 怎么办。它可以是 '2' 或 '' 后跟其他任何内容。首先我尝试了一个非捕获组:

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

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

改为以下工作:

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

我使用了积极的前瞻。我发现的正向前瞻定义如下:

q(?=u) 匹配一个 q 是 后跟一个 u,而不使 u 成为匹配项的一部分。积极的 前瞻结构是一对括号,带有开头 括号后跟问号和等号。

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

【问题讨论】:

  • 'john_1'.split('_').first 是否更适合这种特殊情况?

标签: ruby regex


【解决方案1】:

捕获和匹配是两个不同的东西。 (?:expr) 不捕获 expr,但它仍然包含在匹配的字符串中。零宽度断言,例如(?=expr),不要在匹配的字符串中捕获或包含expr。

也许一些例子将有助于说明差异:

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

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

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

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

【讨论】:

    【解决方案2】:

    您的第一个示例不起作用,因为非捕获组仍然是整体捕获的一部分,而后视仅用于匹配,而不是整体捕获的一部分。

    如果你得到实际的匹配数据,这更容易理解:

    # 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"
    

    【讨论】:

      【解决方案3】:

      让我们考虑几种情况。

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

      str = "john_1"
      

      你有两个选择。

      使用积极的向后看

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

      正向后视要求“john”必须紧跟在下划线之前,但它不是返回的匹配项的一部分。

      使用捕获组:

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

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

      请注意,下划线后面的字符串可能包含下划线:"john_1_a"[/(?&lt;=john)_.+/] #=&gt; "_1_a"。

      如果下划线可以在字符串末尾,则将上述正则表达式中的+替换为*(意思是匹配下划线后面的零个或多个字符)。

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

      str = "john_mary_tom_julie"
      

      我们可以考虑两种情况。

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

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

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

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

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

      这里我们可以写:

      str[/_[^_]+\z/]
        #=> "_julie"
      

      此正则表达式匹配一个下划线,后跟一个或多个非下划线字符,然后是字符串结尾锚点 (\z)。

      旁白:方法String#[]

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

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

      表达式str[/john(_.+)/, 1] 是syntactic sugar 的一个例子(在Ruby 中有很多例子)。在编写 str[...] 时,Ruby 在计算之前将其转换为方法的常规表达式。

      【讨论】:

        猜你喜欢
        • 2011-10-25
        • 2015-12-28
        • 2021-01-14
        • 2013-12-24
        • 2014-07-24
        • 1970-01-01
        • 1970-01-01
        • 2018-02-07
        相关资源
        最近更新 更多