2012-07-11 38 views
7

我想用正則表達式從文本塊中刪除一些東西。我準備好了所有的圖案,但我似乎無法移除重疊的兩個(或更多)圖案。結合多個正則表達式替換

例如:

import re 

r1 = r'I am' 
r2 = r'am foo' 

text = 'I am foo' 

re.sub(r1, '', text) # Returns ' foo' 
re.sub(r2, '', text) # Returns 'I ' 

如何同時更換兩個事件,並用一個空字符串結束了?


我最終使用的Ned Batchelder's answer略加修改:

def clean(self, text): 
    mask = bytearray(len(text)) 

    for pattern in patterns: 
    for match in re.finditer(pattern, text): 
     r = range(match.start(), match.end()) 

     mask[r] = 'x' * len(r) 

    return ''.join(character for character, bit in zip(text, mask) if not bit) 

回答

12

如您所示,您無法通過連續撥打re.sub來完成。您可以使用re.finditer來查找全部。每場比賽將爲您提供一個匹配對象,其中有.start.end屬性指示其位置。您可以將所有這些收集在一起,然後在最後刪除字符。

這裏我使用一個bytearray作爲可變字符串,用作掩碼。它初始化爲零字節,並用'x'標記與任何正則表達式匹配的所有字節。然後,我用的是位掩碼選擇字符,以保持原始字符串,並建立一個新的字符串只用無可比擬的字符:

bits = bytearray(len(text)) 
for pat in patterns: 
    for m in re.finditer(pat, text): 
     bits[m.start():m.end()] = 'x' * (m.end()-m.start()) 
new_string = ''.join(c for c,bit in zip(text, bits) if not bit) 
+0

我從來沒有想過匹配對象的'start'和'end'屬性。我非常確定這會起作用,所以謝謝! – Blender 2012-07-11 22:42:52

+1

很好的答案!我將'()'添加到'start'和'end',因爲這些是方法,而不是屬性。 – georg 2012-07-11 23:00:00

+0

@ thg435:謝謝,我應該測試它! :) – 2012-07-11 23:18:59

2

不是一個令人沮喪,但簡短的回答是,我敢肯定你不能。你可以改變你的正則表達式,使它不需要重疊?

如果你還想這樣做,我會試着跟蹤開始和停止索引在原始字符串上進行的每個匹配。然後通過字符串,只保留字符不在任何刪除範圍內?

1

相當有效率也是一個解決方案,從... Perl的未來結合的正則表達式在一個:

# aptitude install regexp-assemble 
$ regexp-assemble 
I am 
I am foo 
Ctrl + D 
I am(?: foo)? 

正則表達式組裝花費要匹配的正則表達式或字符串的所有變型,然後 在一個將它們組合起來。是的它改變了最初的問題,以一個又一個,因爲它是不是不再匹配重疊的正則表達式,但對於比賽

結合正則表達式,然後你可以在你的代碼中使用它:

$ python 
Python 2.7.3 (default, Aug 1 2012, 05:14:39) 
[GCC 4.6.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import re 
>>> re.sub("I am foo","I am(?: foo)?","") 
'' 

端口的正則表達式::在Python中彙編將是很好:)

+0

該命令是'aptitude install libregexp-assemble-perl'。我無法很快找到您以前指定的名稱的任何一個包的痕跡,但也許您在一個不同的發行版;這對於Debian來說是穩定的。 – tripleee 2016-10-28 06:07:45

+0

此外,在較舊版本的軟件包中,該演示僅在'/ usr/share/doc/libregexp-assemble-perl/examples/assemble.gz'中 - 我想在'squeeze'框中輸入這個腳本,腳本未安裝您指定的名稱。 – tripleee 2016-10-28 06:10:15

1

這是一個替代方案,在選擇器迭代器上使用itertools.compress對文本進行過濾。如果字符應該保留,選擇器返回Trueselector_for_patterns爲每個模式創建一個選擇器。選擇器與全部函數結合在一起(只有當所有模式都希望保留一個字符時,它才應該出現在結果字符串中)。

import itertools 
import re 

def selector_for_pattern(text, pattern): 
    i = 0 
    for m in re.finditer(pattern, text): 
     for _ in xrange(i, m.start()): 
      yield True 
     for _ in xrange(m.start(), m.end()): 
      yield False 
     i = m.end() 
    for _ in xrange(i, len(text)): 
     yield True 

def clean(text, patterns): 
    gen = [selector_for_pattern(text, pattern) for pattern in patterns] 
    selector = itertools.imap(all, itertools.izip(* gen)) 
    return "".join(itertools.compress(text, selector))