正则表达式负先行被忽略
Regex Negative Lookahead Is Being Ignored
我有以下示例文本
[Item 1](./path/notes.md)
[Item 2](./path)
[Item 3](./path/notes.md)
[Item 4](./path)
当我应用以下正则表达式 \[(.*)\].*(?!notes\.md).*\)
时,我希望在打印第一个捕获组时得到以下输出
Item 2
Item 4
但我最终得到的是
Item 1
Item 2
Item 3
Item 4
对我来说,似乎负前瞻部分 (?!notes\.md)
由于某种原因被忽略了,所以正则表达式匹配了整个字符串。
真正让我感到困惑的是,积极的前瞻性工作正如我所期望的那样。例如,在打印第一个捕获组时使用 \[(.*)\].*(?=notes\.md).*\)
returns 以下内容
Item 1
Item 3
这是有道理的,所以我真的很困惑为什么负先行不能正常工作。
这里的问题是 .*
在负前瞻之前是贪婪的,会继续寻找任何东西然后停止。
一种管理方法是将这种贪婪行为包含在负面前瞻中,例如 hère
https://regex101.com/r/yzUQoP/1
/\[(.*)\](?!.*notes\.md)/gm
简而言之,您的 .*
太多了(可能导致 Catastrophic backtracking
,查一下!)。请记住,它们与任何字符匹配零次或多次。这意味着他们将继续尝试匹配,直到获得成功。而且这不一定是你想要的字符数。
解决你的问题的一个方法是把消极的展望移到前面,像这样:
(?!.*notes\.md)\[([^\]]+)\].*
Explanation
:
(?!.*notes\.md)
后跟 'notes.md'
的任意数量的任意字符的否定前瞻
\[
一个[
字符
([^\]]+)
第 1 组,任何字符不是 ]
,一次或多次
\]
一个]
字符
.*
余下的行
使用“multiline
”标志获取每一行。
让我们来看看在第 1 项上匹配您的模式时会发生什么:
\[(.*)\]
匹配 [Item 1]
.*
匹配 (./path/notes.md
- 剩下的字符串现在是
)
(?!notes\.md)
检查剩余字符串是否与模式 notes\.md
匹配。它没有,所以比赛继续。
\)
匹配)
,匹配成功。
如果您更改它,使先行之前的 .*
位于先行内部 (\[(.*)\](?!.*notes\.md).*\)
),它现在将按如下方式工作:
\[(.*)\]
匹配 [Item 1]
- 剩下的字符串现在是
(./path/notes.md)
(?!.*notes\.md)
检查剩余字符串是否与模式 .*notes\.md
匹配,因此匹配失败(更准确地说,正则表达式引擎将在放弃之前尝试回溯匹配,但没有其他方法可以匹配\[(.*)\]
',所以匹配仍然失败。
因此,通过该更改,它将拒绝 notes.md
出现在 )
之前任何位置的所有字符串。如果您希望它仅拒绝 notes.md
直接出现在 )
之前的实例,您可以改用后向(没有 .*
)或将 \)
添加到前向。
您尝试的模式 \[(.*)\].*(?!notes\.md).*\)
从第一个 [
匹配到最后一个 ]
然后发生的是 .*
将匹配该行的其余部分,因此以下断言 (?!notes\.md)
将始终为真,因为该行的其余部分已经匹配。
然后引擎可以回溯匹配最后一个 )
如果你不想在匹配时交叉 []
和 ()
:
\[([^][]+)]\((?![^()]*\bnotes\.md\b)[^()]*\)
\[
匹配 [
([^][]+)
捕获 组 1,匹配 0+ 次除 [
和 ]
之外的任何字符
]\(
匹配 ](
(?!
否定前瞻
[^()]*\bnotes\.md\b
匹配 0+ 次除 (
和 )
之外的任何字符,然后在单词边界之间匹配 notes.md
以防止部分匹配
)
关闭前瞻
[^()]*
匹配除 (
和 )
之外的任何字符 0+ 次
\)
匹配 )
我有以下示例文本
[Item 1](./path/notes.md)
[Item 2](./path)
[Item 3](./path/notes.md)
[Item 4](./path)
当我应用以下正则表达式 \[(.*)\].*(?!notes\.md).*\)
时,我希望在打印第一个捕获组时得到以下输出
Item 2
Item 4
但我最终得到的是
Item 1
Item 2
Item 3
Item 4
对我来说,似乎负前瞻部分 (?!notes\.md)
由于某种原因被忽略了,所以正则表达式匹配了整个字符串。
真正让我感到困惑的是,积极的前瞻性工作正如我所期望的那样。例如,在打印第一个捕获组时使用 \[(.*)\].*(?=notes\.md).*\)
returns 以下内容
Item 1
Item 3
这是有道理的,所以我真的很困惑为什么负先行不能正常工作。
这里的问题是 .*
在负前瞻之前是贪婪的,会继续寻找任何东西然后停止。
一种管理方法是将这种贪婪行为包含在负面前瞻中,例如 hère
https://regex101.com/r/yzUQoP/1
/\[(.*)\](?!.*notes\.md)/gm
简而言之,您的 .*
太多了(可能导致 Catastrophic backtracking
,查一下!)。请记住,它们与任何字符匹配零次或多次。这意味着他们将继续尝试匹配,直到获得成功。而且这不一定是你想要的字符数。
解决你的问题的一个方法是把消极的展望移到前面,像这样:
(?!.*notes\.md)\[([^\]]+)\].*
Explanation
:
(?!.*notes\.md)
后跟 'notes.md'
\[
一个[
字符
([^\]]+)
第 1 组,任何字符不是 ]
,一次或多次
\]
一个]
字符
.*
余下的行
使用“multiline
”标志获取每一行。
让我们来看看在第 1 项上匹配您的模式时会发生什么:
\[(.*)\]
匹配[Item 1]
.*
匹配(./path/notes.md
- 剩下的字符串现在是
)
(?!notes\.md)
检查剩余字符串是否与模式notes\.md
匹配。它没有,所以比赛继续。\)
匹配)
,匹配成功。
如果您更改它,使先行之前的 .*
位于先行内部 (\[(.*)\](?!.*notes\.md).*\)
),它现在将按如下方式工作:
\[(.*)\]
匹配[Item 1]
- 剩下的字符串现在是
(./path/notes.md)
(?!.*notes\.md)
检查剩余字符串是否与模式.*notes\.md
匹配,因此匹配失败(更准确地说,正则表达式引擎将在放弃之前尝试回溯匹配,但没有其他方法可以匹配\[(.*)\]
',所以匹配仍然失败。
因此,通过该更改,它将拒绝 notes.md
出现在 )
之前任何位置的所有字符串。如果您希望它仅拒绝 notes.md
直接出现在 )
之前的实例,您可以改用后向(没有 .*
)或将 \)
添加到前向。
您尝试的模式 \[(.*)\].*(?!notes\.md).*\)
从第一个 [
匹配到最后一个 ]
然后发生的是 .*
将匹配该行的其余部分,因此以下断言 (?!notes\.md)
将始终为真,因为该行的其余部分已经匹配。
然后引擎可以回溯匹配最后一个 )
如果你不想在匹配时交叉 []
和 ()
:
\[([^][]+)]\((?![^()]*\bnotes\.md\b)[^()]*\)
\[
匹配[
([^][]+)
捕获 组 1,匹配 0+ 次除[
和]
之外的任何字符
]\(
匹配](
(?!
否定前瞻[^()]*\bnotes\.md\b
匹配 0+ 次除(
和)
之外的任何字符,然后在单词边界之间匹配notes.md
以防止部分匹配
)
关闭前瞻[^()]*
匹配除(
和)
之外的任何字符 0+ 次
\)
匹配)