替换有时加倍的字符的单个实例

Posted

技术标签:

【中文标题】替换有时加倍的字符的单个实例【英文标题】:Replace single instances of a character that is sometimes doubled 【发布时间】:2016-03-31 14:08:27 【问题描述】:

我有一个字符串,每个字符由管道字符分隔(包括"|"s 本身),例如:

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

我想将所有不在另一个"|" 旁边的"|"s 替换为空,以获得结果:

"funny|boy|a|cat"

我尝试使用mytext.replace("|", ""),但这会删除所有内容并导致一个长字。

【问题讨论】:

每个字符(包括“|”)由“|”字符分隔”是不正确的。如果这是真的,您将拥有"f|u|n|n|y|||b|o|y|||a|||c|a|t" 【参考方案1】:

您可以先用其他东西替换双管,以确保在移除单管后您仍然可以识别它们。然后将它们替换回管道:

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

【讨论】:

'-' -> '|' 预期行为? @Caridorc 我在第二段中解释了这种行为。当然你可以(如果你知道你的输入可能应该)一个更合适的临时值来替换双管道,如果有冲突的话,它会更少。 正确,我建议使用不可打印的字符,不太可能在原始字符串中 现在很清楚了 :) 感谢您花时间详细解释。 @hagello 答案非常清楚地列出了简单(并且非常有效和快速解决方案)的问题,并且不仅建议使用安全的替换值,而且还显示了两个其他没有该问题的解决方案。我不知道这如何使答案总的来说仍然如此糟糕,以至于投反对票甚至是遥不可及的。投反对票说答案有问题,但我不明白如何展示一个简单有效的解决方案、它的缺点和两个替代方案是完全错误的。【参考方案2】:

使用标记值

|| 替换为~。这将记住||。然后删除|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] 

【讨论】:

这比编译正则表达式的正则表达式解决方案快 5 倍,700 ns vs 3.9 µs @Padraic 是的,它们是“正则表达式”;) 如果有人在输入中使用哨兵值,这个解决方案是否仍然有效? @hagello sentinel 值被定义为输入中不存在的值。所以是的,必须谨慎选择哨兵 是的,可读性也很重要,比正则表达式更容易阅读列表组合或替换,当非正则表达式也更快时,想要一个正则表达式没有多大意义【参考方案3】:

您可以使用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'

【讨论】:

st = "f|u|n|n|y||b|o|y||a||c|a|t|" 会失败,您需要抓住结束管道 @PadraicCunningham 是的,我添加了锚点$。感谢您的注意。【参考方案4】:

这可以通过一个相对简单的正则表达式来实现,而不必链接str.replace

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

解释:\|(?!\|) 将寻找一个| 字符,该字符后面没有另一个| 字符。 (?!foo) 表示负前瞻,确保您匹配的任何内容都不会跟随 foo。

【讨论】:

+1 当格式更改为允许- 字符时,此方法不会中断,这与当前投票最多的答案不同。 @BlueRaja-DannyPflughoeft 真的!另请查看 Padraic 的 answer 中的时间比较。问候 @BhargavRao 除非您处理数百万个字符串或数百万个字符的字符串,否则性能无关紧要。【参考方案5】:

使用正则表达式。

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

【讨论】:

您不需要捕获组。【参考方案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'

【讨论】:

哦,re.sub 可能需要一个可调用的...时间来重写一些代码。 +1 你不知道吗?然后你今天学到了一些新东西:-)【参考方案7】:

如果你要使用正则表达式,最快的方法是拆分和加入:

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.replaceapproach 取决于字符串中的内容,但无论字符串中有什么字符,str.split 方法都将起作用。

【讨论】:

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

以上是关于替换有时加倍的字符的单个实例的主要内容,如果未能解决你的问题,请参考以下文章

rename命令

如何用js中的单个字符替换单个数字和两位数字? [复制]

用多个新字符串替换文件中特定位置的单个字符串

用单个空格替换字符串中的多间距 - Python [重复]

如何使用PowerShell替换文件中的多个字符串

电脑系统批处理,如何将多个tab替换为一个tab,而单个的tab不变化?