2010-11-13 122 views
5

我有這個初始字符串。Python中的字符串覆蓋優化

'bananaappleorangestrawberryapplepear' 

,也有使用字符串的元組:

('apple', 'plepe', 'leoran', 'lemon') 

我希望有一個功能,以便從最初的字符串,並用繩子元組我得到這樣的:

'bananaxxxxxxxxxgestrawberryxxxxxxxar' 

我知道如何通過在每個單詞的初始字符串中找到單詞,然後在所有初始字符串中逐個字符地替換單詞來實現這一點。

但它不是非常有效和醜陋。我懷疑應該有一些更優雅的方式,以功能的方式,使用itertools或其他方法。如果您知道一個可以高效完成此操作的Python庫,請告訴我。

UPDATE:賈斯汀皮爾指出了一個案例,我沒有在我最初的問題中描述。如果一個單詞是'aaa'並且'aaaaaa'在初始字符串中,那麼輸出應該看起來像'xxxxxx'。

回答

3
import re 

words = ('apple', 'plepe', 'leoran', 'lemon') 
s = 'bananaappleorangestrawberryapplepear' 

x = set() 

for w in words: 
    for m in re.finditer(w, s): 
     i = m.start() 
     for j in range(i, i+len(w)): 
      x.add(j) 

result = ''.join(('x' if i in x else s[i]) for i in range(len(s))) 
print result 

生產:

bananaxxxxxxxxxgestrawberryxxxxxxxar 
+0

我看到的唯一問題是以下用例:其中一個單詞是'aaa',字符串s ='aaaaa'。這個方法會給出'xxxaa'而不是'xxxxx'的結果,因爲'finditer'找到下一個不重疊的匹配。可能不會出現,但這取決於OP想要做什麼。 – 2010-11-13 19:35:55

+0

是的,我不清楚重疊單詞的情況會發生什麼。 – 2010-11-13 19:42:25

+0

@Justin我沒有想到這種情況,但在字符串'aaaaaa'的情況下,單詞'aaa'應該給'xxxxxx'。但那真的是一個角落案例,如果有更好的事情,我可以和'xxxaa'一起生活。 – 2010-11-13 21:54:37

0
a = ('apple', 'plepe', 'leoran', 'lemon') 
b = 'bananaappleorangestrawberryapplepear' 

for fruit in a: 
    if a in b: 
     b = b.replace(fruit, numberofx's) 

你現在唯一要做的就是確定要用多少個X代替。

+4

這會失敗,因爲它不能保證完全覆蓋,例如, 'apple'和'plepe'重疊,但第二個不會被處理。 – 2010-11-13 18:12:39

0
def mask_words(s, words): 
    mask = [False] * len(s) 
    for word in words: 
     pos = 0 
     while True: 
      idx = s.find(word, pos) 
      if idx == -1: 
       break 

      length = len(word) 
      for i in xrange(idx, idx+length): 
       mask[i] = True 
      pos = idx+length 

    # Sanity check: 
    assert len(mask) == len(s) 

    result = [] 
    for masked, c in zip(mask, s): 
     result.append('x' if masked else c) 

    return "".join(result) 
+0

我不知道這是不是你的意思是「醜陋」,但它是相當快速和可以理解的。如果你正在處理非常大的字符串,而且命中率很低,你可以通過將範圍存儲爲掩碼而不是整個陣列來減少內存使用量,但這裏的性能似乎是合理的。 – 2010-11-13 18:34:09

+0

'pos = idx + length'是錯誤的。只有1位應該被添加到位置,否則使用'yyy'和'yyyyy'失敗。 – 2010-11-13 20:20:59

1

這裏是另一種答案。用x代替這些字母可能會有更快的方法,但我不認爲這是必要的,因爲這已經很快了。

import re 

def do_xs(s,pats): 
    pat = re.compile('('+'|'.join(pats)+')') 

    sout = list(s) 
    i = 0 
    match = pat.search(s) 
    while match: 
     span = match.span() 
     sout[span[0]:span[1]] = ['x']*(span[1]-span[0]) 
     i = span[0]+1 
     match = pat.search(s,i) 
    return ''.join(sout) 

txt = 'bananaappleorangestrawberryapplepear' 
pats = ('apple', 'plepe', 'leoran', 'lemon') 
print do_xs(txt,pats) 

基本上,我創建了一個匹配任何輸入模式的正則表達式模式。然後我只是在最近比賽的開始位置後繼續重新開始搜索。如果你有一個輸入模式是另一個輸入模式的前綴,可能會有問題。

+0

如果您知道如何照顧'xxxa'邊緣案例,請告訴我您的解決方案。 – 2010-11-15 09:18:31

1

假設我們僅限於無STDLIB等進口工作:

s1 = 'bananaappleorangestrawberryapplepear' 
t = ('apple', 'plepe', 'leoran', 'lemon') 
s2 = s1 

solution = 'bananaxxxxxxxxxgestrawberryxxxxxxxar' 

for word in t: 
    if word not in s1: continue 
    index = -1 # Start at -1 so our index search starts at 0 
    for iteration in range(s1.count(word)): 
     index = s1.find(word, index+1) 
     length = len(word) 
     before = s2[:index] 
     after = s2[index+length:] 
     s2 = before + 'x'*length + after 

print s2 == solution 
+0

好吧,建立限制不是問題的一部分,因爲OP提到使用itertools(我懷疑無論如何將工作,因爲我們有兩個參考字符串)。好吧。 – eternicode 2010-11-13 19:47:34

+0

你知道stdlib中的任何內容可以輕鬆完成嗎? – 2010-11-13 21:56:42

+0

你可以用re來縮短它。否則,不。 – eternicode 2010-11-13 22:20:48

1
>>> string_ = 'bananaappleorangestrawberryapplepear' 
>>> words = ('apple', 'plepe', 'leoran', 'lemon') 
>>> xes = [(string_.find(w), len(w)) for w in words] 
>>> xes 
[(6, 5), (29, 5), (9, 6), (-1, 5)] 
>>> for index, len_ in xes: 
... if index == -1: continue 
... string_ = string_.replace(string_[index:index+len_], 'x'*len_) 
... 
>>> string_ 
'bananaxxxxxxxxxgestrawberryxxxxxxxar' 
>>> 

有肯定更有效的方法,但過早的優化是一切罪惡的根源。