Python正则表达式中元字符的转义处理

当你在Python代码中尝试生成正则表达式,一定会遇到这个问题。
(正确答案请直接看最下面黑体字→_→)

假设我们要将一段文本text中的某个关键词keyword替换为repl,因为某些原因不能使用str.replace而需要用到正则表达式,比如只替换keyword位于行首的情况,那可以用下面的代码:

re.sub('^' + keyword, repl, text)

多数时候它能正常工作:

>>> text = 'Result: 42'
>>> keyword = 'Result:'
>>> repl = 'The answer is'
>>> re.sub('^' + keyword, repl, text)
'The answer is 42'

但是当keyword稍微发生一点变化:

>>> text = '[Result] 42'
>>> keyword = '[Result]'
>>> repl = 'The answer is'
>>> re.sub('^' + keyword, repl, text)
'[Result] 42'

代码不工作了!

在这个简单例子里,很容易发现问题所在——在我们构造的正则表达式'^[Result]'中,包含了特殊字符方括号,它们是Python正则表达式中的元字符(metacharacter),含义是匹配括号内的任意一个字符。所以'^[Result]'可以匹配字符串'R...'或者'e...',但不能匹配例子中的'[Result]...'

如何解决?简单,将元字符转义即可:

>>> text = '[Result] 42'
>>> keyword = '\[Result\]'
>>> repl = 'The answer is'
>>> re.sub('^' + keyword, repl, text)
'The answer is 42'

问题在于,keyword通常并非在代码中定义,而是在别处传入或者检测出的,所以我们需要代码自动检测字符串中的元字符,并在每个元字符前面插入转义符号'\'

一个能够完成这项工作的函数:

def escape_metachar(s):
    METACHARS = r'.^$*+?{}[]\|()'
    ret = ''
    for i, c in enumerate(s):
        if c in METACHARS:
            ret += '\\'
        ret += c
    return ret

太丑了,为什么不用正则表达式来做呢。

def escape_metachar(s):
    METACHARS = r'.^$*+?{}[]\|()'
    meta_esc = re.sub('(?P.)', r'\\\g', METACHARS)
    return re.sub('(?P[%s])' % meta_esc, r'\\\g', s)

re.sub有个特殊语法,即'\g'可以引用正则表达式'(?Ppattern)'中匹配到的pattern。上面的代码里,首先在METACHARS中每个字符前面都加个转义,以便在之后的操作中能将其当做普通字符处理,然后在s中查找这些字符,在其前面加转义。

由于第一步跟输入无关,而且METACHARS的定义不太可能发生变化,为了效率起见,写成下面这样更好一点:

def escape_metachar(s):
    METACHARS = r'\.\^\$\*\+\?\{\}\[\]\\\|\(\)'
    return re.sub('(?P[%s])' % METACHARS, r'\\\g', s)

最后,希望你们不要像我一样,写完代码才发现有库函数re.escape

写完代码才发现有库函数re.escape

才发现有库函数re.escape

有库函数re.escape

你可能感兴趣的:(Python)