Python re.sub:在替换字符串中忽略反向引用
我想用一个字符串替换掉某个模式。这个字符串是存储在一个变量里的。这个字符串可能会包含‘\1’,但我希望它不要被当作反向引用来解释,而是简单地当作‘\1’来看待。
我该怎么做呢?
2 个回答
由于一些评论,我思考了很久这个问题并进行了尝试。这让我对转义字符有了更深的理解,所以我几乎完全修改了我的回答,希望对后来的读者有帮助。
NullUserException给出了简短的版本,我会尝试详细解释一下。感谢Qtax和Duncan的批评性反馈,希望这个回答现在是正确且有用的。
反斜杠(\)有特殊的含义,它是字符串中的转义字符。这意味着反斜杠和后面的字符组合在一起形成一个转义序列,当对字符串进行某些操作时,这个序列会被转换成其他东西。这个“某些操作”实际上就是创建字符串。因此,如果你想字面上使用\
,你需要对它进行转义。这个转义字符就是反斜杠本身。
为了更好地理解发生了什么,我先给出一些例子。我还会打印出字符串中字符的ASCII码,希望能帮助理解发生了什么。
s = "A\1\nB"
print s
print [x for x in s]
print [hex(ord(x)) for x in s]
正在打印
A
B
['A', '\x01', '\n', 'B']
['0x41', '0x1', '0xa', '0x42']
所以当我在代码中输入\
和1
时,s
并不包含这两个字符,而是包含了ASCII字符0x01
,它表示“开始标题”。\n
也是一样,它被转换成0x0a
,也就是换行符。
由于这种行为并不总是想要的,可以使用原始字符串,这样转义序列就会被忽略。
s = r"A\1\nB"
print s
print [x for x in s]
print [hex(ord(x)) for x in s]
我只是在字符串前面加了r
,结果现在是
A\1\nB
['A', '\\', '1', '\\', 'n', 'B']
['0x41', '0x5c', '0x31', '0x5c', '0x6e', '0x42']
所有字符都按我输入的那样打印出来。
这就是我们所处的情况。接下来还有另一件事。
可能会有这样的情况:一个字符串应该被传递给正则表达式,要求字面上找到它,因此每个在正则表达式中有特殊含义的字符(例如 +*$[.) 都需要转义,因此有一个特殊的函数re.escape
来完成这个工作。
但对于这个问题来说,这个函数是不对的,因为字符串不应该在正则表达式中使用,而是作为re.sub
的替换字符串。
所以新的情况是:
一个包含转义序列的原始字符串应该用作re.sub
的替换字符串。re.sub
也会处理转义序列,但与之前的处理有一个小但重要的区别:\n
仍然会被转换成0x0a
,也就是换行符,但\1
的处理方式现在改变了!它将被替换为re.sub
中正则表达式捕获组1的内容。
s = r"A\1\nB"
print re.sub(r"(Replace)" ,s , "1 Replace 2")
结果是
1 AReplace
B 2
这个\1
被替换为捕获组的内容,而\n
被替换为换行符。
重要的是,你必须理解这种行为,现在在我看来你有两种选择(我不打算判断哪种是正确的):
创建者对字符串的行为不确定,如果他输入
\n
,那么他想要一个换行。在这种情况下,只需转义后面跟着数字的\
。OnlyDigits = re.sub(r"(Replace)" ,re.sub(r"(\\)(?=\d)", r"\\\\", s) , "1 Replace 2") print OnlyDigits print [x for x in OnlyDigits] print [hex(ord(x)) for x in OnlyDigits
输出:
1 A\1 B 2 ['1', ' ', 'A', '\\', '1', '\n', 'B', ' ', '2'] ['0x31', '0x20', '0x41', '0x5c', '0x31', '0xa', '0x42', '0x20', '0x32']
创建者知道自己在做什么,如果他想要换行,他会输入
\0xa
。在这种情况下,转义所有的All = re.sub(r"(Replace)" ,re.sub(r"(\\)", r"\\\\", s) , "1 Replace 2") print All print [x for x in All] print [hex(ord(x)) for x in All]
输出:
1 A\1\nB 2 ['1', ' ', 'A', '\\', '1', '\\', 'n', 'B', ' ', '2'] ['0x31', '0x20', '0x41', '0x5c', '0x31', '0x5c', '0x6e', '0x42', '0x20', '0x32']
之前的回答提到使用 re.escape()
,但这样会转义太多字符,导致替换后的字符串和被替换的字符串中出现不必要的反斜杠。
在Python中,似乎只需要在替换字符串中对反斜杠进行转义,所以像这样就足够了:
replacement = replacement.replace("\\", "\\\\")
示例:
import re
x = r'hai! \1 <ops> $1 \' \x \\'
print "want to see: "
print x
print "getting: "
print re.sub(".(.).", x, "###")
print "over escaped: "
print re.sub(".(.).", re.escape(x), "###")
print "could work: "
print re.sub(".(.).", x.replace("\\", "\\\\"), "###")
输出:
want to see:
hai! \1 <ops> $1 \' \x \\
getting:
hai! # <ops> $1 \' \x \
over escaped:
hai\!\ \1\ \<ops\>\ \$1\ \\'\ \x\ \\
could work:
hai! \1 <ops> $1 \' \x \\