Python 正则表达式高级应用举例

来源:互联网 发布:照片合成软件ps 编辑:程序博客网 时间:2024/06/06 17:32
    首先,写这篇文章的起因,是这两天玩了一个网站
    http://www.checkio.org
    用Python做题、出题,交流,很有意思也很有挑战。

————————————进入正题——————————————————————————

    其中一道字符串处理的题目如下:
http://www.checkio.org/mission/implementation/1088/python-27/
    简单来说,输入是一个字符串,输出也是一个字符串。我们要做的,是让输入中的整数每隔三位数加一个小数点。

测试用例:
    assert checkio('123456') == '123.456'
    assert checkio('333') == '333'
    assert checkio('9999999') == '9.999.999'
    assert checkio('123456 567890') == '123.456 567.890'
    assert checkio('price is 5799') == 'price is 5.799'
    assert checkio('he was born in 1966th') == 'he was born in 1966th'

    题意应该是表达清楚了。我自己写了一段代码,大概有20~30行的样子,用正则表达式把数字提取出来,然后改掉,然后一段一段填回去。比较麻烦的地方,是修改字符串之后,长度增加了,所以之前在提取字符串的时候要自己处理当前插入位置之类的。

    Pass这道题之后,看了下Best solution:
    def checkio(txt):
        '''
        string with comma separated numbers, which inserted after every third digit from right to left     '''
        return re.sub(r'(?<=\d)(?=(\d\d\d)+\b)', '.', str(txt))

    看过之后发现自己对python的加强版正则表达式不够熟悉,看了看这篇文章,补了补功课:
    Python正则表达式介绍    http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html
    
    上面的解法非常巧妙,注意一点:(?=...)这种匹配模式是不消耗字符的,所以会形成循环匹配,正好达到了目的。我的意思是:如果不是循环匹配的话,加一个小数点之后可能就继续下一个词了。

    经过试验,(?<=...)这种特殊构造有一个限制条件——必须是固定长度的,如果你写(?<=.*?)这种条件,Python会报相应的异常。

————————————re.sub函数————————————————————
    这个题的解法很巧妙,当然巧妙也意味着比较特别,通用性不强。
    回到之前我的那个破代码,贴出来瞅瞅:
def checkio(txt):
    '''
    string with dot separated numbers, which inserted after every third digit from right to left
    '''
    l = []
    beg = 0
    for e in re.finditer(r'\b\d+\b', txt):
        p1, p2 = e.span()
        l.append( txt[beg:p1] )
         
        s = txt[p1:p2]
        s2 = []
        for i in range(len(s)-1, -1, -1):
            s2.append(s[i])            if (len(s)-i) % 3 == 0 and i != 0:

                s2.append('.')
        s2.reverse()
        s = ''.join(s2)
        l.append( s )
         
        beg = p2
    l.append( txt[beg:] )
    print ''.join(l)
    return ''.join(l)
    中间比较长的一段是给字符串加小数点,假如抽出来不看的话,那么多余的地方就是 l、beg、p1、p2等几个变量以及它们的计算了。
    仔细玩了下re.findall和re.sub,发现它们查找的方式不大一样,主要是对括号括起来的group处理不太一样。这个问题以前也纳闷过。今天恍然大悟:

    re.sub的第二个参数repl,可以是函数!

    如果自定义一个函数change(match),在使用re.sub时底层就会在查找到合适的pattern时调用这个change函数。参数是一个match对象,记录了group、span之类的东东。处理这个match对象返回一个字符串,底层就会帮你把这个字符串替换回去。
    不容易说明白,试试就明白了。

    我用这个办法修改了之前MB文件处理的函数,简洁了很多。re.sub函数的接口设计很自然,值得学习。以前竟然没想到能这么用,小自责一下。


原创粉丝点击