如何使用?使正则表达式中的量词变为惰性匹配。

4
最近我一直在研究正则表达式,发现运算符“?”可以使“*”、“+”或“?”成为惰性匹配。我的问题是它是如何做到的?是因为“*?”是一个特殊的运算符,还是“?”对“*”产生了影响?换句话说,正则表达式是否将“*?”识别为一个运算符本身,还是将“*?”识别为两个单独的运算符“*”和“?”?如果“*?”被识别为两个单独的运算符,那么“?”如何影响“*”使其变得惰性呢?如果“?”意味着“*”是可选的,那么这不应该意味着“*”根本不需要存在吗?如果是这样,在语句“.*?”中,正则表达式难道不会只匹配单个字母和整个字符串而不是较短的字符串吗?请解释一下,我急需理解。非常感谢。

1
@TheNewIdiot 单独使用,是的。但在 * 或 + 之后,它会产生不同的效果。然而,我想知道是否有任何正则表达式引擎能够识别 {n,m}? 语法? - PP.
@PP 看起来它是这样工作的 regex101,我认为它基于 PHP。 - Jerry
2
@Uriel,新蠢货在谈论? 量词,它相当于{0,1}量词,就像*相当于{0,}一样。你不能像那样链接量词;这是语法错误。在*?中的?似乎是一个量词修饰符或者(大多数文档都这么说),*?只是另一个像*一样工作但更懒惰的量词。 - Joey
@Joey 我无法想象 ? 会成为量词修饰符。这将意味着在 *? 中,* 不必存在。在使用正则表达式 <.*?> 搜索 <abcda><avfd> 时,这将意味着 * 存在或不存在,这将意味着 <.*?> = <.> 或 <.*>。在搜索中,这将默认为 <.*>,这将意味着 <abcda><avfd> 将是一个有效的匹配。 - Uriel Katz
1
@PP,我认为所有提供非贪婪量词的重要引擎都提供{n,m}?请参见风格比较 - Martin Ender
显示剩余4条评论
4个回答

16
在不同的语境下,? 可以有很多不同的含义。
  • 在普通的正则表达式标记(一个字符、一个简写、一个字符类别、一个组等)之后,它表示“匹配前面的项目0-1次”。
  • ?*+{n,m} 这样的量词符号之后,它具有不同的含义:“使前面的量词变成非贪婪模式而不是默认的贪婪模式(虽然这可以改变,例如在 PHP 中,/U 修改符会使所有的量词都变成非贪婪模式,所以额外的 ? 会使它们变成贪婪模式)。
  • 紧跟在开括号后面,它标志着特殊构造的开始,例如:

    a) (?s):模式修饰符(“打开点任意模式”)
    b) (?:...):使组无捕获
    c) (?=...)(?!...):向前断言
    d) (?<=...)(?<!...):向后断言
    e) (?>...):原子组
    f) (?<foo>...):命名捕获组
    g) (?#comment):内联注释,正则引擎会忽略它们
    h) (?(?=if)then|else):条件语句

还有其他的构造方式,在所有的正则表达式风格中并不都可用。

  • 在字符类别([?])中,它只是匹配一个字面上的 ? 符号。

这个答案包含了我认为是 OP 困惑的关键。具体来说,由编写正则表达式识别器的人(C 和 Python 中的库例程、Perl 中的语言构造等)决定如何解释问号。在不同的实现中,答案是不同的——例如,一些较旧的正则表达式库根本没有 ? 的特殊含义。如果实现者提供“懒惰”的 .*? 匹配,则 ? 通过实现者实现的任何方式使 .* 变得懒惰。如果 OP 正在问“我该如何实现正则表达式”,那就是一个...大的话题。 - torek
2
啊,是的,?? 是一个非贪婪的 ? - PP.

6
我认为一些历史背景可以更好地理解。当 Larry Wall 想要扩展正则表达式语法以支持新特性时,他的选择非常有限。他不能仅仅宣布(例如)% 现在是一个支持新特性“XYZ”的元字符。这将破坏数百万现有的正则表达式,其中一些使用 % 来匹配文字百分号。
他能做的是采取已经定义的元字符,并以这样的方式使用它,使其原来的功能变得没有意义。例如,任何包含两个量词的正则表达式都将无效,因此可以安全地说在另一个量词后面加上 ?,现在它变成了一个勉强量词(比“懒惰”更好的名称,在我看来;非贪婪也不错)。所以你的问题的答案是 ? 不会修改 **? 是一个单一实体:一个勉强量词。在占有量词中 (*+, {0,2}+ 等) 的 + 也是如此。
类似的过程也发生在组语法中。在未转义的开括号后面加量词从来都不合适,因此可以安全地说(?现在标记了特殊组结构的开始。但是,问号本身只支持一种新功能,所以必须至少在?后面跟随一个以上的字符,以指示它是哪种类型的组((?:...)(?<!...)等)。同样,(?:是单个实体:非捕获组的开分隔符。
我不知道他为什么两次都使用问号。我知道Perl 6 Rules(Perl 5正则表达式的自下而上重写)已经摆脱了所有这些乱七八糟的东西,并使用了一个无限更为明智的语法。

3

想象一下,你有以下文本:

BAAAAAAAAD

以下正则表达式将返回:
/B(A+)/ => 'BAAAAAAAA'
/B(A+?)/ => 'BA'
/B(A*)/ => 'BAAAAAAAA'
/B(A*?)/ => 'B'

将“?”添加到“+”和“*”运算符中使它们变得“懒惰”,即它们只匹配使表达式为真所需的绝对最小值。而默认情况下,“*”和“+”运算符是“贪婪”的,并尽可能地匹配更多内容以使表达式为真。
请记住,"+"表示“一个或多个”,因此最小值将是“如果可能,则为1个,如果绝对必要,则为更多”,而最大值将是“如果可能,则为全部,如果绝对必要,则为1个”。
"*"表示“零或多个”,因此最小值将是“如果可能,则为零,如果绝对必要,则为更多”,而最大值将是“如果可能,则为全部,如果绝对必要,则为零”。

1
谢谢你的回答,但这不是我要问的。我想知道在 +* 运算符后添加 ? 如何使它们变成懒惰模式。 - Uriel Katz
1
@UrielKatz,你的问题没有意义。这就是它的语法规定了一个惰性模式,你不需要知道为什么。这就像你问我们“为什么我们使用<br>而不是换行符”。答案很简单:一些人从一开始就规定了跳转到下一行的语法是<br>而不是换行符。 - HamZa

0

这非常取决于实现方式,我想。但是由于我所知道的每个量词都可以用 ? 进行修改,因此以这种方式实现可能是合理的。


那么问题就在于,?如何修改*。正如我向乔伊解释的那样,在使用正则表达式<.*?>搜索<abcda><avfd>时,?表示*存在或不存在,这意味着<.*?>=<.><.*>。在搜索中,这将默认为<.*>,这意味着<abcda><avfd>将是一个有效的匹配。 - Uriel Katz

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