如果我想进行多个字符串替换,最高效的方法是什么?
我在旅行中遇到的一种情况如下:
>>> strings = ['a', 'list', 'of', 'strings']
>>> [s.replace('a', '')...replace('u', '') for s in strings if len(s) > 2]
['a', 'lst', 'of', 'strngs']
如果我想进行多个字符串替换,最高效的方法是什么?
我在旅行中遇到的一种情况如下:
>>> strings = ['a', 'list', 'of', 'strings']
>>> [s.replace('a', '')...replace('u', '') for s in strings if len(s) > 2]
['a', 'lst', 'of', 'strngs']
translate
方法,将单个字符替换为单个字符也是如此。如果输入字符串是Unicode,则除了上述两种“替换”之外,使用translate
方法将单个字符替换为多个字符字符串也是可以的(但如果需要处理字节字符串,则不行)。r'onestring|another|yetanother|orthis'
构建正则表达式(使用垂直线连接要替换的子字符串-当然,如果它们包含特殊字符,还要re.escape
),并编写一个基于字典的简单替换函数。python -mtimeit -s'setup things here' 'statements to check'
(确保要检查的语句没有副作用,以避免扭曲时间测量结果,因为timeit
隐式循环以提供准确的时间测量)。import re
class Replacer(object):
def __init__(self, **replacements):
self.replacements = replacements
self.locator = re.compile('|'.join(re.escape(s) for s in replacements))
def _doreplace(self, mo):
return self.replacements[mo.group()]
def replace(self, s):
return self.locator.sub(self._doreplace, s)
例子用法:
r = Replacer(zap='zop', zip='zup')
print r.replace('allazapollezipzapzippopzip')
如果要替换的一些子字符串是Python关键字,它们需要以略微不同的方式传递,例如下面这样:
r = Replacer(abc='xyz', def='yyt', ghi='zzq')
这会失败,因为def
是一个关键字,所以您需要使用例如:
r = Replacer(abc='xyz', ghi='zzq', **{'def': 'yyt'})
我认为使用类(而不是过程式编程)是一个很好的选择,因为用于定位要替换的子字符串的正则表达式、表示要将其替换成什么的字典以及执行替换的方法真的应该“一起保持”,而类实例恰好是在 Python 中执行这种“保持在一起”的正确方式。闭包工厂也可以使用(因为replace
方法实际上是实例中唯一需要在“外部”可见的部分),但可能不够清晰,难以调试:
def make_replacer(**replacements):
locator = re.compile('|'.join(re.escape(s) for s in replacements))
def _doreplace(mo):
return replacements[mo.group()]
def replace(s):
return locator.sub(_doreplace, s)
return replace
r = make_replacer(zap='zop', zip='zup')
print r('allazapollezipzapzippopzip')
唯一真正的优点可能是略微更好的性能(需要使用timeit
在被认为对使用它的应用程序具有重要意义和代表性的“基准案例”上进行检查),因为在这种情况下对“自由变量”(replacements
、locator
、_doreplace
)的访问可能比在普通的基于类的方法中访问限定名称(例如self.replacements
等)稍微快一些(这是否成立将取决于正在使用的Python实现,因此需要在重要的基准测试中使用timeit
进行检查!)。>>> import re
>>> [re.sub('[aeiou]','',s) for s in strings if len(s) > 2]
['a', 'lst', 'of', 'strngs']
>>> def replacer(s, memo={}):
... if s not in memo:
... memo[s] = re.sub('[aeiou]','',s)
... return memo[s]
...
>>> [replacer(s) for s in strings if len(s) > 2]
['a', 'lst', 'of', 'strngs']
re.sub('[aeiou]', '', s)
会同时替换所有的元音字母吗?如果它是逐个字符检查,我担心 Python 字符串的不可变性。 - Tim McNamara[aeiou]
一次替换所有元音字母。不可变性并不是问题,因为您正在创建新的字符串。 - John La Rooy[]
是一个字符类,它匹配其中任何一个字符并用替换字符串替换该字符。如果所有替换都相同,并且您只匹配单个字符,则此解决方案非常好。 - zdav