gpt4 book ai didi

regex - Perl正则表达式匹配较长句子中的可选短语

转载 作者:行者123 更新时间:2023-12-05 00:19:00 25 4
gpt4 key购买 nike

我试图在句子中匹配一个可选的(可能存在的)短语:

perl -e '$_="word1 word2 word3"; print "1:$1 2:$2 3:$3\n" if m/(word1).*(word2)?.*(word3)/'

输出:
1:word1 2: 3:word3

我知道第一个 '.*' 是贪婪的并将所有内容匹配到 'word3'。使它不贪婪无济于事:
perl -e '$_="word1 word2 word3"; print "1:$1 2:$2 3:$3\n" if m/(word1).*?(word2)?.*(word3)/'

输出:
1:word1 2: 3:word3 

这里似乎存在利益冲突。我原以为 Perl 会匹配 (word2)?如果可能,仍然满足非贪婪的 .*?。至少这是我对“?”的理解。 Perl 正则表达式页面显示“?”制作 1 次或零次,所以它不应该更喜欢一场比赛而不是零次吗?

更令人困惑的是,如果我捕获 .*?:
perl -e '$_="word1 word2 word3"; print "1:$1 2:$2 3:$3 4:$4\n" if m/(word1)(.*?)(word2)?.*(word3)/'

输出:
1:word1 2: 3: 4:word3

这里的所有组都是捕获组,所以我不知道为什么它们是空的。

只是为了确保没有捕获字间空间:
perl -e '$_="word1_word2_word3"; print "1:$1 2:$2 3:$3 4:$4\n" if m/(word1)(.*?)(word2)?.*(word3)/'

输出:
1:word1 2: 3: 4:word3

鉴于唯一未捕获的匹配项是 word2 和 word3 之间的匹配项,我只能假设它是进行匹配的匹配项。
果然:
perl -e '$_="word1_word2_word3"; print "1:$1 2:$2 3:$3 4:$4 5:$5\n" if m/(word1)(.*?)(word2)?(.*)(word3)/'

输出:
1:word1 2: 3: 4:_word2_ 5:word3

所以贪婪匹配是向后工作的,Perl 很乐意匹配 word2 的零个(而不是一个)实例。让它不贪婪也无济于事。

所以我的问题是:我如何编写我的正则表达式来匹配和捕获句子中可能的短语?我这里给出的例子很简单;我解析的实际句子要长得多,我匹配的句子之间有很多词,所以我不能假设中间文本的长度或组成。

非常感谢,
斯科特

最佳答案

背景:懒惰和贪婪的量词是如何工作的
您需要了解贪婪和懒惰量词的工作原理。贪婪的人会立即抓取他们的模式可以匹配的文本,然后引擎将回溯,即它会尝试回到贪婪量化的子模式与子字符串匹配的地方,尝试检查下一个子模式是否可以匹配。
惰性匹配模式只是先匹配最少的字符,然后再尝试匹配其余的子模式。与 *? ,它匹配零个字符,一个空格,然后继续检查下一个模式是否可以匹配,只有如果不能,惰性子模式才会“扩展”以包含一个更多的字符,依此类推。
所以,(word1).*(word2)?.*(word3)将匹配 word2与第一 .* (第二个 .* 将匹配一个空白空间,因为第一个 .* 是贪婪的。虽然您可以认为 (word2)? 是贪婪的,因此必须回溯到,但答案是否定的,因为第一个 .* 被抢了所有的字符串,然后引擎倒退寻找匹配。由于 (word2)? 匹配一个空字符串,所以它总是匹配的,并且 word3 从字符串的末尾开始匹配。参见 this demo 并检查正则表达式调试器部分。
你想过,让我们对第一个 .\*? 使用惰性匹配. (word1).*?(word2)?.*(word3) 的问题(匹配 word2 与第二个 .* 贪婪)有点不同,因为它可以匹配可选组。如何?第一.*?匹配零个字符,然后尝试匹配所有后续子模式。于是,它找到了 word1 ,然后是一个空字符串,并没有找到word2紧随其后 word1 . word2紧随其后 word1 ,将与第一个 .*? 匹配. this demo .
解决方案
目前我看到了两种解决方案,它们都包括使第二个可选组对模式的其余部分“独占”,以便正则表达式引擎在找到时无法跳过它。

  • A branch reset solution provided by Casimir以上。它的缺点是它不能移植到许多其他不支持分支重置的正则表达式风格。请参阅原始答案中的说明。
  • 使用 tempered greedy token : (word1)(?:(?!word2).)*(word2)?.*?(word3) .它比分支重置解决方案效率低,但可以移植到 JS、Python 和大多数其他支持前瞻的正则表达式风格。这是如何运作的? (?:(?!word2).)*匹配除换行符以外的任何字符的 0+ 次出现(使用 /s ,甚至包括换行符),并且不以文字字符序列开头 word2 .如 w匹配,后面不能跟 ord2使构造匹配。因此,当它到达 word2 时,它停止并让后续的子模式 - (word2)? - 匹配并捕获以下 word2 .要使这种方法更有效*,请使用 unroll the loop technique : (word1)[^w]*(?:w(?!ord2)[^w]*)*(word2)?.*?(word3) .
  • 关于regex - Perl正则表达式匹配较长句子中的可选短语,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36770799/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com