ChatGPT解决这个技术问题 Extra ChatGPT

转义正则表达式字符串

我想使用来自用户的输入作为搜索某些文本的正则表达式模式。它有效,但是我如何处理用户在正则表达式中放入有意义的字符的情况?

例如,用户想要搜索 Word (s):正则表达式引擎会将 (s) 作为一个组。我希望它像字符串 "(s)" 一样对待它。我可以在用户输入上运行 replace 并将 ( 替换为 \( 并将 ) 替换为 \) 但问题是我需要对每个可能的正则表达式符号进行替换。

你知道更好的方法吗?

在正则表达式和将模式/捕获组与大字符串匹配的上下文中,这通常有什么用途?
我认为我的回答很好地解释了这些原则:stackoverflow.com/a/73068412/1601580

2
200_success

为此使用 re.escape() 函数:

4.2.3 re Module Contents

escape(string) 返回所有非字母数字反斜杠的字符串;如果您想匹配其中可能包含正则表达式元字符的任意文字字符串,这很有用。

一个简单的示例,搜索任何出现的提供的字符串(可选地后跟“s”),并返回匹配对象。

def simplistic_plural(word, text):
    word_or_plural = re.escape(word) + 's?'
    return re.match(word_or_plural, text)

我不明白为什么这有这么多赞成票。它没有解释为什么或何时我们想要使用转义......甚至没有提到为什么原始字符串是相关的,恕我直言对于理解何时使用它很重要。
N
Neuron

您可以使用 re.escape()

re.escape(string) 返回所有非字母数字反斜杠的字符串;如果您想匹配其中可能包含正则表达式元字符的任意文字字符串,这很有用。

>>> import re
>>> re.escape('^a.*$')
'\\^a\\.\\*\\$'

如果您使用的 Python 版本 < 3.7,这将转义不属于正则表达式语法的非字母数字。

如果您使用的是 Python 版本 < 3.7 但 >= 3.3,这将转义非字母数字,这些非字母数字 不是 正则表达式语法的一部分,除了专门用于下划线 (_)。


传递一个原始字符串是不够的,还是你想匹配文字 ^?我通常使用 re.escape 来强制它匹配我想要匹配的东西,比如括号和空格。
O
Owen

不幸的是,re.escape() 不适合替换字符串:

>>> re.sub('a', re.escape('_'), 'aa')
'\\_\\_'

一种解决方案是将替换放在 lambda 中:

>>> re.sub('a', lambda _: '_', 'aa')
'__'

因为 lambda 的返回值被 re.sub() 视为文字字符串。


re.subrepl 参数是字符串,而不是正则表达式;首先应用 re.escape 没有任何意义。
@tripleee 那是不正确的, repl 参数不是一个简单的字符串,它被解析了。例如,re.sub(r'(.)', r'\1', 'X') 将返回 X,而不是 \1
以下是转义 repl 参数的相关问题:stackoverflow.com/q/49943270/247696
在 3.3 版更改: '_' 字符不再被转义。在 3.7 版更改:Only characters that can have special meaning in a regular expression are escaped.(为什么花了这么长时间?)
S
Stefano Munarini

欧文的回答可能会导致不一致。 lambda 应该只是函数调用的内联替换,但它会产生不同的结果,如下所示。当有人不得不将 lambda 升级为函数调用时,例如为了构建一些额外的复杂性,这会突然崩溃:

import re

xml = """pre@mytag@123@/mytag@post"""

replacewith = '@mytag@456 \\1@/mytag@'

regexp = re.compile(r'@mytag@(.*?)@/mytag@', re.S|re.M|re.I)

def rw(inp):

  return inp

result = regexp.sub(lambda _: replacewith, xml)

print(result) # desired result

result = regexp.sub(rw(replacewith), xml)

print(result) # undesired result

C
Charlie Parker

通常,将您输入正则表达式的字符串转义,使得正则表达式从字面上考虑这些字符。请记住,通常您在计算机中键入字符串,然后计算机插入特定字符。当您在编辑器中看到 \n 时,它并不是真正的新行,直到解析器确定它是。是两个字符。一旦你通过 python 的 print 传递它,它将显示它并因此将它解析为一个新的一行,但在你在编辑器中看到的文本中,它可能只是反斜杠的字符,后跟 n。如果您执行 \r"\n",那么 python 将始终将其解释为您输入的原始内容(据我所知)。更复杂的是,正则表达式还有另一种语法/语法。正则表达式解析器将解释它收到的字符串与 python 的打印不同。我相信这就是为什么我们建议传递像 r"(\n+) 这样的原始字符串 - 以便正则表达式接收您实际输入的内容。但是,正则表达式将收到一个括号,并且不会将其作为文字括号进行匹配,除非您告诉它明确使用 regex 自己的语法规则。为此,您需要 r"(\fun \( x : nat \) :)" 在这里第一个括号不会匹配,因为它是一个捕获组,因为缺少反斜杠,但第二个括号将作为文字括号匹配。

因此,我们通常使用 re.escape(regex) 来转义我们希望按字面解释的内容,即通常会被正则表达式解析器忽略的内容,例如括号、空格等将被转义。例如我在我的应用程序中的代码:

    # escapes non-alphanumeric to help match arbitrary literal string, I think the reason this is here is to help differentiate the things escaped from the regex we are inserting in the next line and the literal things we wanted escaped.
    __ppt = re.escape(_ppt)  # used for e.g. parenthesis ( are not interpreted as was to group this but literally

例如看到这些字符串:

_ppt
Out[4]: '(let H : forall x : bool, negb (negb x) = x := fun x : bool =>HEREinHERE)'
__ppt
Out[5]: '\\(let\\ H\\ :\\ forall\\ x\\ :\\ bool,\\ negb\\ \\(negb\\ x\\)\\ =\\ x\\ :=\\ fun\\ x\\ :\\ bool\\ =>HEREinHERE\\)'
print(rf'{_ppt=}')
_ppt='(let H : forall x : bool, negb (negb x) = x := fun x : bool =>HEREinHERE)'
print(rf'{__ppt=}')
__ppt='\\(let\\ H\\ :\\ forall\\ x\\ :\\ bool,\\ negb\\ \\(negb\\ x\\)\\ =\\ x\\ :=\\ fun\\ x\\ :\\ bool\\ =>HEREinHERE\\)'

我相信存在双反斜杠,以便正则表达式接收文字反斜杠。

顺便说一句,我很惊讶它打印了双反斜杠而不是单个反斜杠。如果有人可以对此发表评论,将不胜感激。我也很好奇现在如何在正则表达式中匹配文字反斜杠。我假设它是 4 个反斜杠,但老实说,由于原始字符串 r 构造,我预计只需要 2 个。


顺便说一句,我很惊讶它打印了双反斜杠而不是单个反斜杠。如果有人可以对此发表评论,将不胜感激。我也很好奇现在如何在正则表达式中匹配文字反斜杠。我假设它是 4 个反斜杠,但老实说,由于原始字符串 r 构造,我预计只需要 2 个。
g
guru

请试一试:

\Q 和 \E 作为锚点

放置一个 Or 条件来匹配一个完整的单词或正则表达式。

参考链接:How to match a whole word that includes special characters in regex