2015-12-25 14 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

没错,我们现在有结果证明也:) –