2007-06-22

 

Python Cookbook 1.18 在单一字符串中进行多模式替换

需求:

需要对一个字符串进行多个字串的替换.

讨论:

在某些情况下,正则式是处理字符串问题最快的方法.re库的sub方法也使得在使用正则式处理字符串替换时变得异常强大.下面给出的代码可以在一个字符串内替换参数中的字典所包含的所有字符串:

import re
def multiple_replace(text, adict):
    rx = re.compile('|'.join(map(re.escape, adict)))
    def one_xlat(match):
        return adict[match.group(0)]
    return rx.sub(one_xlat, text)

这个例子演示了如果使用正则式来进行单一字符串的多模式替换.假设有一个字典,里面的键是要替换的字符串,值是要替换为的字符串.可以使用多次replace来实现这个需求.显然 ,使用上面的代码更简便一些.
首先,我们根据要替换的键构造一个正则表达式.在上例中,使用"|"来分隔不同的模式.接下来,我们没有将要替换的字符串传递给re.sub方法,而是交给了一个回调方法one_xlat.这样,re.sub会对每一次匹配调用这个方法,并传递一个re.MatchObject实例做为它的参数.在我们的例子中,这个方法返回了匹配字符串在字典中对应的字符串.
在上例中,每一次调用multiple_replcace方法,都要重新构造正则式和定义one_xlat,有时,我们想一次做好预处理工作,进行多次替换,那可以使用下面的方法:

import re
def make_xlat(*args, **kwds):
    adict = dict(*args, **kwds)
    rx = re.compile('|'.join(map(re.escape, adict)))
    def one_xlat(match):
        return adict[match.group(0)]
    def xlat(text):
        return rx.sub(one_xlat, text)
    return xlat

调用make_xlat方法,可以传入一个字典做参数,也可以传入和dict()类似的参数来构造一个字典.make_xlat类似一个方法工厂.返回了构造好的满足我们需要的方法.下面是这个方法的使用示例,我们使用Python常用的测试方法:

if _ _name_ _ == "_ _main_ _":
    text = "Larry Wall is the creator of Perl"
    adict = {
      "Larry Wall" : "Guido van Rossum",
      "creator" : "Benevolent Dictator for Life",
      "Perl" : "Python",
    }
    print multiple_replace(text, adict)
    translate = make_xlat(adict)
    print translate(text)

有事替换是针对单词的,而不是简单的字符串替换,为了满足这个需求,我们可以使用正则式的r'\b'语法,把我们上面构造正则式的语句替换为:

rx = re.compile(r'\b%s\b' % r'\b|\b'.join(map( re.escape, adict)))

其余的代码不用改变.但这也不是一个好消息,假如我们每次要自定义正则式,有很多代码都是要"复制粘贴"的,这样对代码的重用很不好,日后也不好维护.
写"好代码"的关键是:"一次,仅仅一次!",既然我们发现上面代码有很多重复的,我们需要调整一下.将方法工厂改写成一个类,这样每次构造新的替换方法时,只要重载就可以了:

class make_xlat:
    def _ _init_ _(self, *args, **kwds):
        self.adict = dict(*args, **kwds)
        self.rx = self.make_rx( )
    def make_rx(self):
        return re.compile('|'.join(map(re.escape, self.adict)))
    def one_xlat(self, match):
        return self.adict[match.group(0)]
    def _ _call_ _(self, text):
        return self.rx.sub(self.one_xlat, text)

这样写完之后,我们前面的测试代码一点都不需要改变,而灵活性却大大增强了,要构造一个针对单词替换的方法,我们可以这样写:

class make_xlat_by_whole_words(make_xlat):
    def make_rx(self):
        return re.compile(r'\b%s\b' % r'\b|\b'.join(map(re.escape, self.adict)))

使用类替代方法,可以有效的减少代码的"复制粘贴",也更好的使用了面向对象方法,当然,代码的重用并不是简简单单的将方法封装成类,还有很多高级的特征,有兴趣的话,可以参考面向对象相关章节.

相关说明:

sub(pattern, repl, string, count=0)
    Return the string obtained by replacing the leftmost
    non-overlapping occurrences of the pattern in string by the
    replacement repl.  repl can be either a string or a callable;
    if a callable, it's passed the match object and must return
    a replacement string to be used.

dict(mapping) -> new dictionary initialized from a mapping object's
       (key, value) pairs.

compile(pattern, flags=0)
    Compile a regular expression pattern, returning a pattern object.

标签:


Comments: 发表评论



<< Home

This page is powered by Blogger. Isn't yours?