夜阑卧听风吹雨

铁马冰河入梦来

Loading

Python re.sub 会对要替换成的字符串进行转义问题

衍生问题:re.error: bad escape \x at position xxx (line xz, column xz)

我先把定义放在这:re.sub(pattern, repl, string, count=0, flags=0)
出现这个问题的时候,我搜索了一下,结合我的情况:我的 pattern 是没有进行错误的转义的。可能出错的就是在 repl 里。翻看源代码:

def sub(pattern, repl, string, count=0, flags=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 string, backslash escapes in it are processed.  If it is
    a callable, it's passed the Match object and must return
    a replacement string to be used."""
    return _compile(pattern, flags).sub(repl, string, count)

分析

re.sub(pattern, repl, string, count=0, flags=0) 会对 repl 内的反斜杠进行转义,这是一个容易被忽略值得注意的问题。

一方面 sub 通过转义反斜杠,实现反向引用。另一方面却可能会影响我们单纯字面意义上的反斜杠,比如 \\ 会被最终转义成 \,如果我们处理的是代码字符串,就会导致问题出现,例如:

import re

# 直接将整个字符串替换
origin = "/*  好的  */"
to = r"('\\', 0)"
print("想要替换成:", to)
print("实际上替换成:", re.sub(r"/*  好的  */", to, origin))
想要替换成: ('\\', 0)
实际上替换成: ('\', 0)

如果要替换成的字符串 repl 再复杂一些,可能会产生一些错误的转义,抛出形如异常:

re.error: bad escape \x at position 86013 (line 1575, column 7966)

看到一种方法:import regex as re,可以跳过这个异常,但是实际上也没有完成我们的最初替换需求。

解决

可以手动再次转义 repl 字符串,但是实际上这个工作得不偿失。

结合源代码,repl 可以是字符串也是可调用的,比如函数。如果是字符串就会对反斜杠进行转义。如果是函数,会被传递 Match 对象,函数需要返回要替换成的字符串。

因此我们就可以自定义一个函数来解决 re.sub 自动转义的问题:

import re

origin = "/*dfe1683替换区  \n好的   */"
to = r"('\\', 0)"
print("想要替换成:", to)
print("实际上替换成:", re.sub(r"/\*dfe1683替换区[\s\S]+?\*/", lambda m: to, origin))
想要替换成: ('\\', 0)
实际上替换成: ('\\', 0)

也就是说,把我们要替换成的不需要转义斜杠的字符串,从 to 改成匿名函数 lambda m: to。这个方法相比转义 repl 的字符串,避免了两次转义斜杠的性能浪费。

posted @ 2021-04-10 11:56  二次蓝  阅读(1827)  评论(0编辑  收藏  举报