【问题标题】:Question marks in regular expressions正则表达式中的问号
【发布时间】:2011-07-31 19:31:01
【问题描述】:

我正在阅读regular expressions reference,我在想?和 ??人物。你能用一些例子解释一下它们的用处吗?我还不够了解。

谢谢

【问题讨论】:

  • 您使用正则表达式的目标编程语言是什么?正则表达式在不同语言中的行为略有不同。
  • 我在python、C#、php、perl、visual basic、grep中使用了正则表达式。

标签: regex regex-greedy


【解决方案1】:

这是一个很好的问题,我花了一段时间才明白自己懒惰的?? 量词的意义。

? - 可选(贪婪)量词

? 的用处很容易理解。如果你想同时找到httphttps,你可以使用这样的模式:

https?

此模式将匹配两个输入,因为它使 s 成为可选。

?? - 可选(惰性)量词

?? 更微妙。它通常与? 做同样的事情。当您询问以下问题时,它不会改变真/假结果:“此输入是否满足此正则表达式?” 相反,它与以下问题相关:“此输入的哪个部分与此正则表达式匹配,以及哪些部分属于哪些组?” 如果输入可以通过多种方式满足模式,引擎将根据???(或*)决定如何对其进行分组对比*?,或+对比+?)。

假设您有一组要验证和解析的输入。这是一个(诚然愚蠢的)示例:

Input:       
http123
https456
httpsomething

Expected result:
Pass/Fail  Group 1   Group 2
Pass       http      123
Pass       https     456
Pass       http      something

你首先想到的是this

^(http)([a-z\d]+)$
Pass/Fail  Group 1   Group 2    Grouped correctly?
Pass       http      123        Yes
Pass       http      s456       No
Pass       http      something  Yes

它们都通过了,但你不能使用第二组结果,因为你只想要第 2 组中的456

好吧,让我们try again。假设第 2 组可以是字母或数字,但不能同时是:

(https?)([a-z]+|\d+)
Pass/Fail  Group 1   Group 2   Grouped correctly?
Pass       http      123       Yes
Pass       https     456       Yes
Pass       https     omething  No

现在第二个输入没问题,但第三个分组错误,因为默认情况下? 是贪婪的(+ 也是,但? 排在第一位)。在确定shttps? 还是[a-z]+|\d+ 的一部分时,如果结果是通过任一方式,正则表达式引擎将始终选择左边的那个。所以第 2 组输了s,因为第 1 组吃光了。

要解决此问题,您可以创建 one tiny change:

(https??)([a-z]+|\d+)$
Pass/Fail  Group 1   Group 2    Grouped correctly?
Pass       http      123        Yes
Pass       https     456        Yes
Pass       http      something  Yes

本质上,这意味着:“如果必须匹配 https,但看看当第 1 组只是 http 时这是否仍然通过。” 引擎意识到 s 可以作为[a-z]+|\d+ 的一部分工作,因此它更愿意将其放入第 2 组。

【讨论】:

  • 在所有情况下,https??([a-z]+|\d+)http([a-z]+|\d+)(在捕获之前根本没有)给出相同的匹配和捕获。所以我看不出这是一个有意义的例子。
  • 你的回答也很好。其实我只有问题?? :-) 并且正在寻找与相反的不同之处? .
  • @Matthew http([a-z]+|\d+)https(456) 不匹配。这就是区别。
  • @xralf,没有。它们都匹配完全相同的匹配并捕获:With ??Without
  • @Matthew Flaschen - 对于该输入,它们的工作方式相同。 http([a-z]+|\d+)$ 将不匹配 https456https??([a-z]+|\d+)$ 将会,并且仍然有 https456 的预期结果。这就是区别。
【解决方案2】:

??? 之间的主要区别在于它们的懒惰?? 是懒惰的,? 不是。

假设您想在正文中搜索单词“car”,但不想仅限于单数“car”;您还想匹配复数“汽车”。

这是一个例句:

I own three cars.

现在,如果我想匹配单词“car”我只想得到字符串“car”作为回报,我会像这样使用懒惰的??

cars??

这说,“寻找汽车或汽车这个词;如果找到,请返回car,仅此而已”。

现在,如果我想匹配相同的词(“car”或“cars”)我想得到整个匹配作为回报,我会使用非惰性 @ 987654329@像这样:

cars?

这就是说,“寻找汽车或汽车一词,然后返回汽车或汽车,无论你找到什么”。

在计算机编程的世界中,惰性通常意味着“仅根据需要进行评估”。所以懒惰的?? 只返回匹配所需的数量;因为 "cars" 中的 "s" 是可选的,所以不要返回它。另一方面,非惰性(有时称为贪婪)操作会尽可能多地求值,因此? 返回所有匹配项,包括可选的“s”。

就我个人而言,我发现自己使用 ? 作为一种使其他正则表达式运算符(例如 *+ 运算符)变得懒惰的方式比我将它用于简单的字符可选性的频率更高,但是 YMMV。

在代码中查看

以下是上面在 Clojure 中实现的示例:

(re-find #"cars??" "I own three cars.")
;=> "car"

(re-find #"cars?" "I own three cars.")
;=> "cars"

项目re-find 是一个函数,它将其第一个参数作为正则表达式#"cars??" 并返回它在第二个参数"I own three cars." 中找到的第一个匹配项

【讨论】:

  • 您的cars?? 示例是正确的,但它返回的结果与您仅使用car 相同。您可能需要一个不同的示例来证明 ?? 的有用性。
  • @Justin,没错,但你的也有同样的问题。
  • @Matthew Flaschen - 当您省略 s?? 时,我的答案中的第三个输入字符串会产生相同的结果,但其他字符串不会。这就是它与将可选元素排除在模式之外的不同之处:使相同的模式适用于所有三个输入字符串。
  • @semperos 嗨,如果我要检查零次或一次出现的字符是 ? 本身怎么办?
  • @VaradBhatnagar 您需要在正则表达式中转义 ? 字符。作为 Clojure 中的一个示例,如果您想匹配字符串 foo?,您可以使用 (re-find #"foo\?" "foo?"),其中 \? 会转义正则表达式中的问号,以便将其按字面意思处理,而不是作为正则表达式运算符。
【解决方案3】:

正则表达式中问号的其他一些用法

除了其他答案中的解释外,问号在正则表达式中还有 3 种用法。

  1. 负预测

    如果您愿意,可以使用负前瞻 匹配没有被其他东西跟随的东西。消极的 前瞻结构是一对括号,带有开头 括号后跟一个问号和一个感叹号。 x(?!x2)

    例子

    • 考虑一个词There
    • 现在,默认情况下,RegEx e 将在单词 There 中找到第三个字母 e

      There
        ^
      
    • 但是,如果您不想要紧跟在r 后面的e,那么您可以使用正则表达式e(?!r)。现在的结果是:

      There
          ^
      
  2. 正向预测

    正向预测的工作原理相同。 q(?=u) 匹配 q 紧随其后的是 u,而不是 u 的一部分 匹配。正向前瞻结构是一对括号, 带左括号,后跟问号和 等号。

    例子

    • 考虑一个词getting
    • 现在,默认情况下,RegEx t 将在单词 getting 中找到第三个字母 t

      getting
        ^
      
    • 但是,如果您想要 t 后面紧跟 i,那么您可以使用 RegEx t(?=i)。现在的结果是:

      getting
         ^
      
  3. 非捕获组

    每当您在括号() 中放置正则表达式时,它们 创建一个编号的捕获组。它存储字符串的一部分 匹配的正则表达式中的部分 括号。

    如果你不需要组来捕获它的匹配,你可以优化 把这个正则表达式改成

    (?:Value)
    

另请参阅thisthis

【讨论】:

    【解决方案4】:

    ? 只是将前一项(字符、字符类、组)设为可选:

    colou?r
    

    匹配“颜色”和“颜色”

    (swimming )?pool
    

    匹配“a pool”和“spooling”

    ?? 是一样的,但它也是惰性的,所以如果可能的话,项被排除。正如那些文档所指出的,??在实践中很少见。我没用过。

    【讨论】:

    • 参考文献里也写了。
    • @xralf,在文档和我的示例之后还有什么不清楚的地方吗?
    • @Matthew:你只是改写了参考文档,没有解释它。特别是比赛中的包含/排除是一个令人费解的概念。
    • @Lars,对不起?我确实举例说明了。
    • @Matthew 我的评论可能越过了您的编辑;但请参阅其他答案以获得关于懒惰与贪婪的良好解释。
    【解决方案5】:

    使用不情愿的量词“一次或根本不”匹配 X?? 运行 Oracle 文档中的测试工具表明它可以作为保证始终为空的匹配。

    $ java RegexTestHarness
    
    Enter your regex: x?
    Enter input string to search: xx
    I found the text "x" starting at index 0 and ending at index 1.
    I found the text "x" starting at index 1 and ending at index 2.
    I found the text "" starting at index 2 and ending at index 2.
    
    Enter your regex: x??
    Enter input string to search: xx
    I found the text "" starting at index 0 and ending at index 0.
    I found the text "" starting at index 1 and ending at index 1.
    I found the text "" starting at index 2 and ending at index 2.
    

    https://docs.oracle.com/javase/tutorial/essential/regex/quant.html

    它似乎与空匹配器相同。

    Enter your regex:     
    Enter input string to search: xx
    I found the text "" starting at index 0 and ending at index 0.
    I found the text "" starting at index 1 and ending at index 1.
    I found the text "" starting at index 2 and ending at index 2.
    
    Enter your regex: 
    Enter input string to search: 
    I found the text "" starting at index 0 and ending at index 0.
    
    Enter your regex: x??
    Enter input string to search: 
    I found the text "" starting at index 0 and ending at index 0.
    

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 1970-01-01
      • 2012-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多