懒惰匹配和先行断言

6
我正在为验证C#的URL编写正则表达式。现在,我需要的正则表达式不能匹配其他的http://,但必须匹配URL中第一个http://。这是我第一次尝试的正则表达式:
(https?:\/\/.+?)\/(.+?)(?!https?:\/\/)

但是这个正则表达式不起作用(即使删除(?!https?:\/\/))。以这个输入字符串为例:
http://test.test/notwork.http://test

这是我的第一个疑问:为什么捕获组(.+?)不能匹配notwork.http://test?懒惰量词应该尽可能少地匹配,但为什么不到结尾呢?在这种情况下,我肯定漏掉了什么(起初,我认为它可能与回溯有关,但我认为这不是问题),所以我阅读了这篇文章,找到了一个解决方案,即使我不确定这是否是最好的解决方案,因为它说:

这种技术在懒惰点星上没有任何优势

无论如何,那个解决方案是温和的点号。这是我的下一个尝试:

(https?:\/\/.+?)\/((?:(?!https?:\/\/).)*)

现在,这个正则表达式可以工作,但不是我想要的方式。我需要一个只有在URL有效时才匹配的结果。
顺便说一下,我认为我还没有完全理解新的正则表达式的作用:为什么负向先行断言留在点之前而不是之后?所以我试着把它移到点之后,看起来它匹配URL直到找到第二个http之前的倒数第二个字符。回到更正的正则表达式,我的假设是负向先行断言实际上正在尝试检查正则表达式已经读取的点后面的内容,这样对吗?
其他解决方案也很好接受,但我首选理解这个。谢谢。

问题太宽泛了。第二个“疑问”在这里有解释:https://dev59.com/n10Z5IYBdhLWcg3w6j-_。至于第一个问题,你只需要使用一个正向前瞻和$作为替代方案((.*?)(?=https?:\/\/|$))。.+?匹配1个字符,并且不必匹配更多,因为它是懒惰的。 - Wiktor Stribiżew
“当URL有效时,您所需的是什么意思?” - Wiktor Stribiżew
关于第一个疑问:我应该使用 $,这样懒惰量词才能匹配到输入的结尾,对吗?为什么它不是隐含的呢?我读了你关于“温和贪婪标记”的答案,现在更清楚了。我只需要在 URL 不包含其他 http:// 时进行匹配,而使用我的当前正则表达式时,当包含 http:// 时也会有匹配。顺便说一下,谢谢你的回答。 - Marco Luzzara
看起来你想要类似于(?>https?://\S+?/(?:(?!https?://).)*)(?!https?://)的东西。 - Wiktor Stribiżew
你说得对。再次感谢你。 - Marco Luzzara
1个回答

2
您所需要的解决方案是:
(?>https?://\S+?/(?:(?!https?://).)*)(?!https?://)

请查看正则表达式演示细节
  • (?>https?://\S+?/(?:(?!https?://).)*) - 原子组(不允许回溯到其子模式)匹配
    • https?:// - http://https://
    • \S+? - 任何1个或多个非空格字符,尽可能少地匹配,直到第一个...
    • / - 后面跟着/符号...
    • (?:(?!https?://).)* - 零个或多个字符(尽可能多),不以http://https://字符序列开头。
  • (?!https?://) - 负向前瞻,如果当前位置右侧紧跟着http://https://,匹配失败。

(https?:\/\/.+?)\/(.+?)(?!https?:\/\/)不起作用是因为.+?模式是“懒惰”的,即它抓取它找到的第一个字符,然后让随后的子模式匹配。随后的子模式是一个负向前瞻,只有在当前位置右侧没有http://https://子字符串时才会匹配失败。由于在http://test.test/notwork.http://test中的n后面没有这样的子字符串,所以返回以n结尾的匹配项,匹配成功。如果您不告诉正则表达式引擎匹配更多内容,或者匹配到其他分隔符/模式,它就不会匹配。

温和的贪婪令牌解决方案已经被广泛讨论。关于在哪里放置前瞻的确切疑问在这个答案中有所涉及。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接