愚蠢的问题在这里......我试图匹配线内的空白区域,而忽略前导空格/标签并提出这些正则表达式字符串,但我可以&#39 ;弄清楚为什么只有一个工作(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]+
优先于预测,但我怎样才能理性化呢?< / p>
答案 0 :(得分:2)
简而言之:
负面的背后检查只是检查当前位置是否前面没有lookbehind模式,并且检查结果是 true (是的,继续匹配)或 false (停止处理模式,进行下一场比赛)。检查不影响正则表达式索引,执行检查后引擎保持在同一位置。
在当前表达式中,首先检查外观模式 (因为模式从从左到右解析,反之亦然),并且仅在后备检查时返回true,尝试[ \t]+
模式。在第一个表达式中,否定 lookbehind返回 false ,因为lookbehind模式找到匹配项(字符串的开头)。第二个表达式否定 lookbehind返回 true ,因为字符串的开头没有字符串的开头,后面跟着一个或多个空格/制表符。
以下是两个表达式背后的逻辑:
首先执行lookbehind检查。在第一个表达式中,(?<!^[ \t]*)
尝试在字符串的开头匹配。字符串的开头没有字符串的开头(^
),后跟0 +空格或制表符。值得注意的是,.NET中的lookbehind实现会以相反的方向检查字符串,翻转字符串,并搜索零个或多个选项卡以及字符串边界。在(?<!^[ \t]*)
的情况下,lookbehind返回false,因为在0个空格或制表符之前有一个起始位置(注意我们仍然在字符串的开头)。第二个表达式lookbehind,(?<!^[ \t]+)
,返回true,因为在字符串的第0个索引处的字符串开头之前没有制表符或空格,因此[ \t]+
消费模式抓取前导水平空格。这会进一步移动正则表达式索引,稍后会在字符串中找到另一个匹配。
在字符串开头失败后,第一个表达式尝试在第一个空格后匹配。但是,(?<!^[ \t]*)
返回false,因为字符串的开头后跟1个空格(第一个)。同样的故事在第二个空间之后重复。与第一个(?<!^[ \t]*)[ \t]+
表达式匹配的唯一空格是那些不在字符串开头的空格。
Lookahead类比
检查类似的前瞻模式:[ \t]+(?![ \t]+$)
模式将在"bb bb "
中找到两个空白块,而[ \t]+(?![ \t]*$)
将与字符串末尾的那些不匹配。同样的逻辑适用:1)*
版本允许匹配空字符串,因此找到字符串的结尾而负向前导返回false,匹配失败。当+
版本遇到并消耗尾随空格时,停留在字符串末尾的正则表达式引擎找不到一个或多个空格/制表符后跟另一个字符串结尾,因此,否定前瞻返回true并且尾随空格是匹配的。