如何使正则表达式匹配为非贪婪模式?

5
我在编程书中读到,.*?通常会使正则表达式变得非贪婪,从而匹配最短的可能匹配项。
然而,对于以下内容,它并不能按照预期工作:
正则表达式:http.*?500.jpg
测试字符串:http://google.com<img src="http://33.google.com/image/500.jpg
我想要匹配的是最短的字符串:http://33.google.com/image/500.jpg
但是它没有。它匹配了整个字符串...
我尝试着更深入地阅读正则表达式,但是我还没有能力解决它。
如何像这个例子一样只选择最短的字符串匹配?

http://regex101.com/r/bG8gT3 - jonrsharpe
这里真正的问题是你试图使用正则表达式来解析HTML。http://blog.codinghorror.com/parsing-html-the-cthulhu-way/ - jedmao
@mrjedmao 不适用。 - Zig Mandel
4个回答

9
我知道已经有两个答案了,但有时候从另一个角度来看问题并处理问题会更有帮助。 问题 当引擎定位在第一个之前时,它会尽力匹配正则表达式 http.*?500.jpg。在那个位置上,这个正则表达式能够匹配吗?是的,它可以。在匹配到http后,引擎一直惰性匹配,直到遇到500.jpg为止。没有任何东西可以阻止它。你告诉引擎只匹配必要的字符,它正在执行它被告知的任务。
相比之下,假设你有一个包含两个500.jpg的字符串。
http://google.com<img src="http://google.com/500.jpg 1500.jpg 
                                                    ^ lazy .*? stops here
                                                             ^ greedy .* stops here

贪婪匹配会匹配整个字符串。但是懒惰匹配会尽快停止:在原处停止。这就是贪婪和懒惰之间的区别。

解决方法:不要使用点星 - 使用正确的令牌

假设您知道每个http字符串后面有一个空格或换行符。您可以使用http\S*?\.jpg进行懒惰匹配。关键是\S*不能跳过空格,而点星可以。

参考文献

此外,我强烈建议您阅读下面的文章,它应该有助于解决任何剩余的困惑。

正则表达式的许多层次的贪婪程度


2

http 尽可能早地匹配,然后 .*? 尽可能少地匹配,导致字符串比必要的更长。

相反,您可以在它之前添加贪婪的 .* 来确保 http 尽可能晚地匹配:

import re
str = 'http://google.com<img src="http://33.google.com/image/500.jpg'
re.match('.*(http.*?500.jpg)', str).groups()[0]

1
正则表达式引擎从左到右逐个字符处理字符串。因此,当找到第一个http时,正则表达式引擎会尝试使用尽可能少的字符使模式成功,但是从当前位置开始(换句话说:在字符串中尽快匹配)。
对于您的示例,为了确保匹配以500.jpg结尾的url,您可以通过提供更多信息来帮助正则表达式引擎找到所需内容,例如:
\bhttp://\S+/500\.jpg\b

新增信息:

  • 使用单词边界 \b
  • http:// 更加明确
  • \S+ 利用url中没有空格的事实 (通常将空格转换为 %20)
  • 文件名前的斜杠

注意: 当您在模式中添加更多信息时,您会发现有时懒惰量词是无用的。

这只是符合您摘录的示例。您需要根据自己的情况进行调整。(假设字符串由逗号分隔的URL组成,在这种情况下,应将 \S 替换为 [^\s,])


0
import re
str = 'http://google.com<img src="http://33.google.com/image/500.jpg'

#by using findall() function...
exact_url=re.findall(r"^http:.*?(http://.*/500.jpg)$",str)[0]

#by using match() function...
exact_url=re.match(r"^http:.*(http://.*)$",str).group(1)

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