可变宽度负向外观中的Star vs. plus量词

愚蠢的问题在这里…我试图匹配线内的空白区域而忽略前导空格/标签并提出这些正则表达式字符串,但我无法弄清楚为什么只有一个工作(C#正则表达式引擎) :

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

请注意负面预测中的明星重复。 将这些与" word1 word2" (2个前导空格)匹配时:

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

为什么只有版本1.(星号)在这里工作而版本2.(加号)与第二个领先空间不匹配?

我认为这是因为贪婪的优先级+来自[ \t]+不是前瞻,但我怎么能理性化呢?

简而言之

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

在当前表达式中, 首先检查lookbehind模式(因为模式从左到右解析,反之亦然),并且只有当lookbehind检查返回true时才尝试[ \t]+模式。 在第一个表达式中, 向lookbehind返回false,因为lookbehind模式找到匹配(字符串的开头)。 第二个表达式向lookbehind返回true,因为没有字符串的开头,后面跟着一个字符串开头的一个或多个空格/制表符。

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

  • 首先执行lookbehind检查。 在第一个表达式中, (?尝试在字符串的开头匹配。 字符串的开头没有字符串的开头( ^ ),后跟0 +空格或制表符。 重要的是要注意.NET中的lookbehind实现检查相反方向的字符串,翻转字符串,并搜索零个或多个选项卡和字符串边界。 在(? ,lookbehind返回false,因为在0个空格或制表符之前有一个起始位置(注意我们仍然在字符串的开头)。 第二个表达式lookbehind, (? ,返回true,因为在字符串的第0个索引处的字符串开头之前没有制表符或空格,因此, [ \t]+消耗模式抓住领先的水平空白。 这会进一步移动正则表达式索引,并且稍后会在字符串中找到另一个匹配项。

  • 在字符串开头失败后,第一个表达式尝试在第一个空格后匹配。 但是, (?返回false,因为有一个字符串后跟1个空格(第一个)。 同样的故事在第二个空间之后重复。 与第一个(?表达式匹配的唯一空格是那些不在字符串开头的空格。

Lookahead比喻

检查类似的先行模式: [ \t]+(?![ \t]+$)模式将在"bb bb "找到两个空白块,而[ \t]+(?![ \t]*$)将不匹配字符串末尾的那些。 相同的逻辑适用:1) *版本允许匹配空字符串,因此找到字符串的结尾并且否定前瞻返回false,匹配失败。 当+版本遇到并使用尾随空格时,停留在字符串末尾的正则表达式引擎找不到一个或多个空格/制表符后跟另一个字符串结尾,因此,负向前导返回true并且尾随空格匹配。