与正则表达式的复杂非贪婪匹配

use*_*666 5 python regex non-greedy

我正在尝试从HTML表中解析行,其中包含在Python中使用正则表达式的特定值的单元格.我在这个(人为的)例子中的目标是获得带有"牛"的行.

import re

response = '''
<tr class="someClass"><td></td><td>chicken</td></tr>
<tr class="someClass"><td></td><td>chicken</td></tr>
<tr class="someClass"><td></td><td>cow</td></tr>
<tr class="someClass"><td></td><td>cow</td></tr>
<tr class="someClass"><td></td><td>cow</td></tr>
'''

r = re.compile(r'<tr.*?cow.*?tr>', re.DOTALL)

for m in r.finditer(response):
  print m.group(0), "\n"
Run Code Online (Sandbox Code Playgroud)

我的输出是

<tr class="someClass"><td></td><td>chicken</td></tr> <tr class="someClass"><td></td><td>chicken</td></tr> <tr class="someClass"><td></td><td>cow</td></tr>

<tr class="someClass"><td></td><td>cow</td></tr>

<tr class="someClass"><td></td><td>cow</td></tr>

虽然我的目标是获得

<tr class="someClass"><td></td><td>cow</td></tr>

<tr class="someClass"><td></td><td>cow</td></tr>

<tr class="someClass"><td></td><td>cow</td></tr>

我明白非贪心?在这种情况下不起作用,因为回溯是如何工作的.我摆弄着负面的外观和前瞻但却无法让它发挥作用.

有人有建议吗?

我知道像Beautiful Soup等解决方案,但问题是关于理解正则表达式,而不是问题本身.

解决人们对不使用HTML正则表达式的担忧.我想要使​​用正则表达式来解决的一般问题是来自

response = '''0randomstuffA1randomstuff10randomstuffA2randomstuff10randomstuffB3randomstuff10randomstuffB4randomstuff10randomstuffB5randomstuff1'''
Run Code Online (Sandbox Code Playgroud)

输出

0randomstuffB3randomstuff1 

0randomstuffB4randomstuff1 

0randomstuffB5randomstuff1 
Run Code Online (Sandbox Code Playgroud)

和randomstuff应解释为随机字符串(但不包含0或1).

Cas*_*yte 4

您的问题与贪婪无关,而是与正则表达式引擎尝试在字符串中从左到右的每个位置取得成功这一事实有关。这就是为什么你总是会获得最左边的结果,并且使用非贪婪量词不会改变起始位置!

如果您编写类似以下内容:<tr.*?cow.*?tr>0.*?B.*?1 (对于第二个示例),首先尝试模式:

  <tr class="someClass"><td></td><td>chicken</td></tr>...
# ^-----here

# or

  0randomstuffA1randomstuff10randomstuffA2randomstuff10randomstuffB3ra...
# ^-----here
Run Code Online (Sandbox Code Playgroud)

第一个.*?会吃掉字符,直到“cow”或“B”。结果,第一场比赛是:

<tr class="someClass"><td></td><td>chicken</td></tr>
<tr class="someClass"><td></td><td>chicken</td></tr>
<tr class="someClass"><td></td><td>cow</td></tr>
Run Code Online (Sandbox Code Playgroud)

对于你的第一个例子,并且:

0randomstuffA1randomstuff10randomstuffA2randomstuff10randomstuffB3randomstuff1
Run Code Online (Sandbox Code Playgroud)

对于第二个。


为了获得您想要的结果,您需要使模式在字符串中不需要的位置失败。这样做.*?是没有用的,因为太宽容了。

例如,您可以禁止 a</tr>或 a1出现在“cow”或“B”之前。

# easy to write but not very efficient (with DOTALL)
<tr\b(?:(?!</tr>).)*?cow.*?</tr>

# more efficient
<tr\b[^<c]*(?:<(?!/tr>)[^<c]*|c(?!ow)[^<c]*)*cow.*?</tr>

# easier to write when boundaries are single characters
0[^01B]*B[^01]*1
Run Code Online (Sandbox Code Playgroud)