可变宽度负后视中的星号与加号量词
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 +)
请注意 star 与 plus 重复 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 匹配。
这里的愚蠢问题...我试图在行内匹配 white-space 而 忽略前导 spaces/tabs 并想出了这些正则表达式字符串,但我不明白为什么只有一个在工作(C# 正则表达式引擎):
(?<!^[ \t]*)[ \t]+ // regex 1. (with *)
(?<!^[ \t]+)[ \t]+ // regex 2. (with +)
请注意 star 与 plus 重复 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 匹配。