簡體   English   中英

與正則表達式的復雜非貪婪匹配

[英]Complex non-greedy matching with regular expressions

我正在嘗試從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"

我的輸出是

<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'''

輸出

0randomstuffB3randomstuff1 

0randomstuffB4randomstuff1 

0randomstuffB5randomstuff1 

和randomstuff應解釋為隨機字符串(但不包含0或1)。

您的問題與貪婪無關,而是與正則表達式引擎嘗試從左到右在字符串中的每個位置成功。 這就是為什么你總是得到最左邊的結果,使用非貪婪的量詞不會改變起始位置!

如果您寫的內容如下: <tr.*?cow.*?tr>0.*?B.*?1 (對於您的第二個示例) ,首先嘗試使用模式:

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

# or

  0randomstuffA1randomstuff10randomstuffA2randomstuff10randomstuffB3ra...
# ^-----here

第一個.*? 會吃掉字符直到“牛”或“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>

為你的第一個例子,和:

0randomstuffA1randomstuff10randomstuffA2randomstuff10randomstuffB3randomstuff1

為了第二個。


要獲得所需的內容,需要使模式在字符串中不需要的位置失敗。 要做到這一點.*? 因為過於寬容而沒用。

例如,你可以禁止在“牛”或“B”之前發生</tr>1

# 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

如果輸入字符串在單獨的行中包含每個標記,則Moses Koledoye的答案將起作用。
但是,如果標簽分布在多行上,則需要以下內容:

import re


response = '''
<tr class="someClass
"><td></td><td>chicken</td></tr><tr class="someClass"><td></td><td>chic
ken</td></tr><tr class="someClass"><td></td><td>cow</td></tr><tr class="someC
lass"><td></td><td>cow</td></tr><tr
class="someClass"><td></td><td>c
ow
</td></tr>
'''


# Remove all the newlines
# Required only if words like 'cow' and '<tr' are split between 2 lines
response = response.replace('\n', '')

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

for m in r1.finditer(response):
    n = r2.match(m.group())
    if n:
        print n.group(), '\n'

請注意,即使標記位於您提供的示例字符串中所示的單獨行上,這也會起作用,因此這是一種更通用的解決方案。

如果您的“響應”字符串始終包含換行符,那么您可以在沒有正則表達式的情況下執行所需操作。 使用內置split功能創建每行的列表。 然后迭代列表,看看'cow'是否在行中:

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>
'''

lines = response.split('\n')
cows = []
for line in lines:
    if 'cow' in line:
        cows.append(line)
print(cows)

輸出:

['<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>']

你根本不需要正則表達式。

只要你加上? 對你的表達量詞,你已經使令牌變得懶惰(非貪婪)。

無論如何,你可以這樣做:

for line in example:
    if 'cow' in line:
        print(line)

不需要正則表達式。

如果你想知道“非貪婪”的匹配是做什么的,它會這樣做:

import re

lazy = r'[a-z]*?b'
#             ^^ lazy
greedy = r'[a-z]*b'
#               ^ greedy

string = 'aaabbbaaabbb'

print(re.match(lazy, string))
print(re.match(greedy, string))

產量

<_sre.SRE_Match object; span=(0, 4), match='aaab'>
<_sre.SRE_Match object; span=(0, 12), match='aaabbbaaabbb'>

請注意,第一場比賽將匹配,直到遇到第一場'b'。 那是因為它試圖盡可能少地匹配(懶惰)。

貪婪的匹配將匹配到最后一個'b',因為它嘗試匹配盡可能多的次數。

兩個匹配都將“根據需要回饋”,也就是說,如果有其他令牌可以匹配,則可以使用這些匹配。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM