可变宽度负后视中的星号与加号量词

Star vs. plus quantifier in the variable-width negative lookbehind

这里的愚蠢问题...我试图在行内匹配 white-space 而 忽略前导 spaces/tabs 并想出了这些正则表达式字符串,但我不明白为什么只有一个在工作(C# 正则表达式引擎):

(?<!^[ \t]*)[ \t]+       // regex 1. (with *)
(?<!^[ \t]+)[ \t]+       // regex 2. (with +)

请注意 starplus 重复 negative look-ahead。将这些与 " word1 word2" 匹配时(2 个前导 spaces):

⎵⎵word1⎵word2             
      ^                  // 1 match for regex 1. (*)

⎵⎵word1⎵word2             
^^     ^                 // 2 matches for regex 2. (+)
 ^     ^                 // why not match like this?

为什么只有版本 1.(星号)在这里工作而版本 2.(加号)不匹配第二个前导 space?

我认为这是因为来自 [ \t]+ 的贪婪 + 比前瞻的优先级更高,但我如何合理地期待这一点?

简而言之:

负向回溯只是检查 当前位置 之前是否没有回溯模式,并且检查的结果是 true (是的,继续匹配)或 false(停止处理模式,进行下一个匹配)。检查不影响正则表达式索引,引擎在执行检查后保持在同一个位置。

在当前表达式中,首先检查后向模式(因为模式是从左到右解析的,反之亦然), 并且仅当 lookbehind 检查 returns true 时才会尝试 [ \t]+ 模式。在第一个表达式中,negative lookbehind returns false 作为 lookbehind 模式找到匹配项(字符串的开头)。第二个表达式 negative lookbehind returns true 因为没有字符串的开头后跟 1 个或多个 spaces/tabs字符串的开头。

下面是 2 个表达式背后的逻辑:

  • 首先执行回顾检查。在第一个表达式中,(?<!^[ \t]*) 试图匹配字符串的开头。字符串的开头没有字符串开头 (^) 后跟 0+ space 或制表符。请务必注意,.NET 中的后向执行会检查相反方向的字符串、翻转字符串并搜索零个或多个制表符和字符串边界。在 (?<!^[ \t]*) 的情况下,lookbehind returns false 因为在 0 spaces 或制表符之前有一个起始位置(注意我们仍然在字符串的开头)。第二个表达式 lookbehind,(?<!^[ \t]+),returns true,因为在字符串的第 0 个索引处的字符串开始之前没有制表符或 space,因此,[ \t]+消费模式抢先横白space。这进一步移动了正则表达式索引,并且稍后在字符串中找到了另一个匹配项。

  • 字符串开头失败后,第一个表达式尝试匹配第一个space之后。但是,(?<!^[ \t]*) returns 为假,因为字符串开头后跟 1 space(第一个)。第二个 space 之后的位置重复相同的故事。与第一个 (?<!^[ \t]*)[ \t]+ 表达式匹配的唯一 space 是那些不在字符串开头的

前瞻类比

检查类似的先行模式:[ \t]+(?![ \t]+$) pattern will find both whitespace chunks in "bb bb ", while [ \t]+(?![ \t]*$) 将不匹配字符串末尾的模式。同样的逻辑适用:1)*版本允许匹配一个空字符串,因此找到字符串的结尾并且否定前瞻returns false,匹配失败。当 + 版本遇到并消耗尾随的白色 spaces 时,停留在字符串末尾的正则表达式引擎无法找到 1 个或多个 spaces/tabs 后跟字符串的另一端,因此, 否定前瞻 returns true 和尾随白色 space 匹配。