2015-12-25 42 views
20

我具有與每個字符的字符串通過管道字符(包括"|"š本身)被分離的字符的單個實例,例如:替換有時加倍

"f|u|n|n|y||b|o|y||a||c|a|t" 

我想更換所有"|" S的不相鄰的另一個"|"什麼也沒有,得到結果:

"funny|boy|a|cat" 

我試着用mytext.replace("|", ""),但除去一切,使得一個長字。

+14

「*」字符(包括「|」)被「|」字符*「分隔」並不是真的。如果那是真的,你就會有''f | u | n | n | y ||| b | o | y ||| a ||| c | a | t「'。 – zvone

回答

24

您可以先用別的東西替換雙管,以確保在刪除單管後仍可識別它們。然後你替換這些回管:

>>> t = "f|u|n|n|y||b|o|y||a||c|a|t" 
>>> t.replace('||', '|-|').replace('|', '').replace('-', '|') 
'funny|boy|a|cat' 

你應該儘量選擇一個替代值是一個安全的臨時值,並且不自然地出現在你的文字。否則,即使原本不是雙管,也會遇到衝突,即該字符被替換。因此,如果您的文本可能包含破折號,請不要像上面那樣使用破折號。您也可以一次使用多個字符,例如:'<THIS IS A TEMPORARY PIPE>'

如果你想完全避免這種衝突,你也可以解決這個完全不同的問題。例如,你可以用雙管第一分割字符串並在每個子進行置換,最終將它們連接到一起:

>>> '|'.join([s.replace('|', '') for s in t.split('||')]) 
'funny|boy|a|cat' 

當然,你也可以使用正則表達式來替換那些管道後面沒有其他管道:

>>> import re 
>>> re.sub('\|(?!\|)', '', t) 
'funny|boy|a|cat' 
+0

是''''''''''''期望的行爲? – Caridorc

+0

@Caridorc我在第二段解釋了這種行爲。當然,如果有任何衝突,你可以(也可能應該如果你知道你的輸入)更好的臨時值來代替雙管道。 – poke

+0

正確,我建議使用不可打印的字符,非常不可能在原始字符串中 – Caridorc

29

使用Sentinel值

~更換||。這將記住||。然後刪除| s。最後用|重新替換它們。

>>> s = "f|u|n|n|y||b|o|y||a||c|a|t" 
>>> s.replace('||','~').replace('|','').replace('~','|') 
'funny|boy|a|cat' 

另一個更好的辦法是使用的事實,他們幾乎替代文本。解決的辦法是讓他們完全替代的......

s.replace('||','|||')[::2] 
+3

這比正則表達式解決方案快5倍,編譯後的正則表達式'700 ns vs 3.9μs' –

+1

@Padraic Yep,它們是「Regex」;) –

+0

如果有人在輸入中使用sentinel值,這個解決方案是否仍然有效? – hagello

11

您可以使用正則表達式positive look ahead更換的後邊帶有字母字符的點子:

>>> import re 
>>> st = "f|u|n|n|y||b|o|y||a||c|a|t" 
>>> re.sub(r'\|(?=[a-z]|$)',r'',st) 
'funny|boy|a|cat' 
+0

這對於'st =「f | | | n | n | y || b | o | y || a || c | a | t |「',你需要抓住結尾管道 –

+2

@PadraicCunningham是的,我添加了錨點'$'。謝謝你的提示。 – Kasramvd

30

這可以用一個相對可以實現簡單的regex,而不必鏈str.replace

>>> import re 
>>> s = "f|u|n|n|y||b|o|y||a||c|a|t" 
>>> re.sub('\|(?!\|)' , '', s) 
'funny|boy|a|cat' 

說明:\ |將尋找一個| characte |(\?!) r之後不是另一個|字符。 (?!foo)意味着消極的前瞻,確保你所匹配的任何東西都不會跟着foo。

+6

+1當格式更改爲允許使用'-'字符時,此方法不會中斷,這與當前頂級答案不同。 –

+0

@ BlueRaja-DannyPflughoeft True!另外看看Padraic的[時間比較](http://stackoverflow.com/a/34472163/4099593)。 Regards –

+0

@BhargavRao除非您正在處理數百萬個字符串或數百萬字符的字符串,否則性能無關緊要。 –

6

使用正則表達式。

import re 

line = "f|u|n|n|y||b|o|y||a||c|a|t" 
line = re.sub("(?!\|\|)(\|)", "", line) 

print(line) 

輸出:

funny|boy|a|cat 
+0

你不需要一個捕獲組。 –

6

一個與捕獲組另一個正則表達式選項。

>>> import re 
>>> re.sub(r'\|(\|?)', r'\1', "f|u|n|n|y||b|o|y||a||c|a|t") 
'funny|boy|a|cat' 

說明:

\| - 匹配所有管道字符。 (\|?) - 捕獲以下管道字符(如果存在)。然後用\1替換比賽將爲您帶來第一個捕獲組的內容。所以在單個點的位置,它會給出一個空字符串,並在||,它會帶來第二個管道字符。

通過字和非字邊界另一個技巧...

>>> re.sub(r'\b\|\b|\b\|\B', '', "f|u|n|n|y||b|o|y||a||c|a|t|") 
'funny|boy|a|cat' 

另一個使用一個負回顧後..

>>> re.sub(r'(?<!\|)\|', '', "f|u|n|n|y||b|o|y||a||c|a|t|") 
'funny|boy|a|cat' 

獎金......

>>> re.sub(r'\|(\|)|\|', lambda m: m.group(1) if m.group(1) else '', "f|u|n|n|y||b|o|y||a||c|a|t") 
'funny|boy|a|cat' 
+0

哦,'re.sub'可以用一個可調用的...時間來重寫一些代碼。 +1 – timgeb

+0

你不知道嗎?然後你今天學到了新的東西:-) –

4

如果你將使用正則表達式,這是最快的方法是分裂和加入:

In [18]: r = re.compile("\|(?!\|)") 

In [19]: timeit "".join(r.split(s)) 
100000 loops, best of 3: 2.65 µs per loop 
In [20]: "".join(r.split(s)) 
Out[20]: 'funny|boy|a|cat' 
In [30]: r1 = re.compile('\|(?!\|)') 

In [31]: timeit r1.sub("", s) 
100000 loops, best of 3: 3.20 µs per loop 

In [33]: r2 = re.compile("(?!\|\|)(\|)") 
In [34]: timeit r2.sub("",s) 
100000 loops, best of 3: 3.96 µs per loop 

str.splitstr.replace方法仍然較快:

In [38]: timeit '|'.join([ch.replace('|', '') for ch in s.split('||')]) 
The slowest run took 11.18 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 1.71 µs per loop 

In [39]: timeit s.replace('||','|||')[::2] 
1000000 loops, best of 3: 536 ns per loop 

In [40]: timeit s.replace('||','~').replace('|','').replace('~','|') 
1000000 loops, best of 3: 881 ns per loop 

根據什麼可以是字符串將決定str.replace辦法,但str.split方法將工作無論是在字符串中哪些字符。

+1

沒錯,我們現在有結果證明也:) –