正则表达式中必须转义的特殊字符是什么?

468

我已经厌倦了在使用许多正则表达式实现时,总是试图猜测是否应该转义特殊字符如'()[]{}|'等。

例如Python、sed、grep、awk、Perl、rename、Apache、find等都不同。是否有规则集可以告诉我何时应该转义特殊字符,何时不应该转义? 是否取决于正则表达式类型,如PCRE、POSIX或扩展正则表达式?


5
好的正则表达式库拥有像“escape()”这样的函数,允许使用任意字符串作为正则表达式的一部分。 - ivan_pozdeev
2
你可以使用在线的正则表达式检查工具,例如http://gskinner.com/RegExr/(它是免费的)。 (输入正则表达式后,将鼠标悬停在其上即可查看) - hexicle
2
转义所有非字母数字字符。句号。 - salmatron
2
这个问题已被添加到Stack Overflow 正则表达式 FAQ中的“其他”分类下。 - aliteralmind
1
此问题已添加到Stack Overflow正则表达式FAQ,位于“转义序列”下。 - aliteralmind
显示剩余3条评论
13个回答

428

你需要转义哪些字符,以及哪些字符不需要转义,实际上取决于你使用的正则表达式引擎。

对于 PCRE 和大多数称为 Perl 兼容的引擎,在字符类之外需要转义以下字符:

.^$*+?()[{\|

还有这些字符类里面的内容:

^-]\

对于POSIX扩展正则表达式(ERE),在字符类之外需要转义这些字符 (与PCRE相同):

.^$*+?()[{\|

在 POSIX ERE 中,转义任何其他字符都是错误的。

在字符类内部,反斜杠是 POSIX 正则表达式中的字面字符。您不能使用它来转义任何内容。如果要将字符类元字符包含为文字字符,则必须使用“巧妙的放置”。 将 ^ 放在任何位置除了开头,将 ] 放在开头,将 - 放在字符类的开头或结尾以匹配这些文字,例如:

[]^-]
在 POSIX 基本正则表达式(BRE)中,这些是元字符,需要转义才能取消它们的含义:
.^$*[\

在BRE中转义括号和花括号会使它们具有ERE中未转义版本的特殊含义。某些实现(例如GNU)在转义时还会赋予其他字符特殊含义,例如\?和+。除了. ^ $ * () {}之外转义其他字符通常是BRE中的错误。

在字符类中,BRE遵循与ERE相同的规则。

如果所有这些让您感到头晕,请获取RegexBuddy副本。在“创建”选项卡上,单击“插入令牌”,然后选择“字面量”。RegexBuddy将根据需要添加转义。


13
在我提到的所有正则表达式语言中,/不是元字符,因此正则表达式语法不需要对其进行转义。当在编程语言中引用正则表达式时,该语言的字符串或正则表达式格式规则可能需要对/"'进行转义,甚至可能需要双重转义\\ - Jan Goyvaerts
3
“冒号”在字符类内外都需要转义吗?http://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions 上说:“PCRE具有一致的转义规则:任何非字母数字字符都可以被转义为其字面值[...]”。 - nicolallias
5
"可能被转义"并不等同于"应该被转义"。PCRE语法从不要求对字面上的冒号进行转义,因此对字面上的冒号进行转义只会让你的正则表达式更难阅读。 - Jan Goyvaerts
1
对于非POSIX ERE(我最常使用的一种,因为它是Tcl实现的),转义其他内容不会生成错误。 - slebetman
对于 JavaScript 开发者:const escapePCRE = string => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");,来源于 Mozilla 开发者网络 - Константин Ван

84

现代正则表达式引擎(PCRE)

包括C、C++、Delphi、EditPad、Java、JavaScript、Perl、PHP(preg)、PostgreSQL、PowerGREP、PowerShell、Python、REALbasic、Real Studio、Ruby、TCL、VB.Net、VBScript、wxWidgets、XML Schema、Xojo、XRegExp。
PCRE兼容性可能会有所不同。

    任何地方都可以使用: . ^ $ * + - ? ( ) [ ] { } \ |


传统正则表达式引擎(BRE/ERE)

包括awk、ed、egrep、emacs、GNUlib、grep、PHP(ereg)、MySQL、Oracle、R、sed。
在后续版本或使用扩展程序中可以启用PCRE支持。

ERE/awk/egrep/emacs

    字符类外部: . ^ $ * + ? ( ) [ { } \ |
    字符类内部: ^ - [ ]

BRE/ed/grep/sed

    字符类外部: . ^ $ * [ \
    字符类内部: ^ - [ ]
    对于字面量,不需要转义:+ ? ( ) { } |
    对于标准正则表达式行为,需要转义:\+ \? \( \) \{ \} \|


注意事项

  • 如果对特定字符不确定,可以使用\xFF进行转义
  • 字母数字字符不能用反斜杠进行转义
  • 在PCRE中,任意符号都可以用反斜杠转义,但在BRE/ERE中不行(只有必要时才需转义)。对于PCRE,只有字符类内部的] -需要进行转义,但我将它们放在单个列表中以保持简洁。
  • 引用表达式字符串也必须转义周围的引号,并且通常需要反斜杠加倍(例如在JavaScript中,"(\")(/)(\\.)"/(")(\/)(\.)/之间的区别)。
  • 除了转义字符外,不同的正则表达式实现可能支持不同的修饰符、字符类、锚点、量词和其他功能。有关更多详细信息,请查看 regular-expressions.info,或使用regex101.com 来测试您的表达式。

  • 1
    你的回答中有很多错误,包括但不限于:你所谓的“现代”正则表达式风格在字符类外部不需要转义-]。POSIX(BRE/ERE)在字符类内部没有转义字符。Delphi RTL中的正则表达式风格实际上是基于PCRE的。Python、Ruby和XML都有自己的正则表达式风格,更接近于PCRE而不是POSIX风格。 - Jan Goyvaerts
    1
    @JanGoyvaerts 感谢您的纠正。您提到的这些 flavor 确实更接近 PCRE。至于转义字符,我之所以保留它们是为了简单起见;在任何地方都进行转义比例外几个情况要容易记住得多。如果高级用户想要避免一些反斜杠,他们会知道该怎么做。无论如何,我更新了我的答案,并加入了一些澄清,希望能解决一些问题。 - Beejor
    我已经试图寻找这个好几天了!你是最棒的! - Caterina
    你需要在字符类中同样转义 "" 吗? - Caterina
    还有单引号、双引号和“/”怎么办?在BRE和ERE语法中,如何获取它们的字面值? - Caterina
    @Caterina 你可以用反斜杠来转义斜杠。正则表达式不需要引号进行转义。希望这能帮到你! - Beejor

    23

    1
    Addedbytes的速查表过于简化,并且存在一些明显的错误。例如,它说\<\>是单词边界,这只有在Boost正则表达式库中才是正确的(据我所知)。但是在其他地方,它说<>是元字符,必须转义(为\<\>)才能按字面意义匹配它们,这在任何情况下都不是正确的。 - Alan Moore

    7

    POSIX识别基本正则表达式(BRE)和扩展正则表达式(ERE)的多种变体。即使如此,由于POSIX标准化的实用程序的历史实现,仍存在一些怪癖。

    没有一个简单的规则来确定何时使用哪种符号表示法,甚至无法确定给定命令使用哪种符号表示法。

    请查看Jeff Friedl的Mastering Regular Expressions书籍。


    5

    很不幸,像(和\(这样的符号在Emacs风格的正则表达式和大多数其他风格之间的含义是相反的。因此,如果你试图转义它们,你可能会做与你想要的相反的事情。

    所以你真的需要知道你要引用哪种风格。


    4

    4

    实际上,没有一个固定的正则表达式语法。目前已有约数千种不同的正则表达式语法,主要分为Perl、EMACS/GNU和AT&T等几种类型,但我也经常会感到惊讶。


    4
    有时候,使用您列出的字符进行简单转义是不可能的。例如,在sed中,使用反斜杠来转义括号在替换字符串的左侧是行不通的。
    sed -e 's/foo\(bar/something_else/'
    

    我倾向于使用简单的字符类定义,因此上面的表达式变成了:
    sed -e 's/foo[(]bar/something_else/'
    

    我发现这个方法适用于大多数正则表达式实现。

    顺便说一下,字符类是相当普通的正则表达式组件,因此它们往往在需要在正则表达式中转义字符的大多数情况下都能工作。

    编辑:在下面的评论之后,我想提一下,在查看正则表达式评估行为时,您还必须考虑有限状态自动机和非有限状态自动机之间的区别。

    您可能会想看看“闪亮的球书”(Effective Perl)(经过消毒的亚马逊链接),特别是关于正则表达式的章节,以了解正则表达式引擎评估类型的差异。

    并非所有的世界都是PCRE!

    无论如何,与SNOBOL相比,正则表达式都很笨重!那真是一门有趣的编程课程!连同Simula一起学习。

    啊,70年代末在UNSW学习的乐趣!(-:


    'sed' 是一个命令,其中普通的 '(' 不是特殊字符,但 '\(' 是特殊字符;相反,PCRE 反转了这个意义,所以 '(' 是特殊字符,但 '\(' 不是。这正是 OP 所询问的内容。 - Jonathan Leffler
    sed是一个*nix实用程序,它使用最原始的正则表达式评估之一。PCRE不涉及我所描述的情况,因为它涉及一种不同类别的(无)有限自动机以评估正则表达式。我认为我的建议仍然适用于最小的正则表达式语法集。 - Rob Wells
    1
    在符合POSIX标准的系统上,sed使用POSIX BRE,我在我的答案中进行了介绍。现代Linux系统上的GNU版本使用带有一些扩展的POSIX BRE。 - Jan Goyvaerts

    2
    要知道何时以及如何转义而不进行尝试,需要准确理解字符串经过的上下文链。您将从最远的一侧指定该字符串到其最终目的地,即由正则表达式解析代码处理的内存。
    请注意,内存中的字符串是如何处理的:它可以是代码中的普通字符串,也可以是输入到命令行中的字符串,但可以是交互式命令行或在shell脚本文件中声明的命令行,或者是由代码提到的内存变量中的(字符串)参数,或包含任何类型的封装动态生成的代码的字符串...
    每个上下文都分配了一些具有特殊功能的字符。
    当您想要传递字符而不使用其特殊功能(局部于上下文),那么您必须对其进行转义,以供下一个上下文使用...可能需要一些其他转义字符,这些字符可能还需要在前面的上下文中进行转义。
    此外,还可能存在诸如字符编码之类的东西(最阴险的是utf-8,因为它对于常见字符看起来像ASCII,但根据终端的设置,它可能会被可选地解释,因此可能会表现不同,然后是HTML / XML的编码属性,必须准确理解该过程。
    例如,在命令行中以perl -npe开头的正则表达式需要转换为一组exec系统调用,这些系统调用将文件句柄作为管道连接,每个这些系统调用只有由(未转义)空格分隔的参数列表,并且可能是管道(|)和重定向(> N> N>&M),括号,*?的交互扩展,$(())...(所有这些都是*sh使用的特殊字符,可能会干扰下一个上下文中的正则表达式字符,但它们按顺序评估:在命令行之前。命令行由bash / sh / csh / tcsh / zsh等程序读取,本质上在双引号或单引号内,转义更简单,但不必在命令行中引用字符串,因为大多数情况下必须在空格前加上反斜杠,而引号不是必需的,使字符*和?的扩展功能可用,但是在引号中解析为不同的上下文。然后,当评估命令行时,内存中获得的正则表达式(不是按照命令行编写的)接收与在源文件中一样的处理。
    对于正则表达式,方括号[ ]内有字符集上下文,perl正则表达式可以由大量非字母数字字符引用(例如m //或m:/better/for/path:...)。
    您可以在其他答案中了解有关字符的更多详细信息,这些字符非常特定于最终的正则表达式上下文。正如我所指出的,您提到您发现正则表达式转义是通过尝试进行的,这可能是因为不同的上下文具有不同的一组字符,这使您的尝试记忆混淆了(通常在这些不同的上下文中使用反斜杠作为转义字面字符而不是其功能)。

    2
    对于PHP来说,“在非字母数字字符前加上“\”以指定它表示自身的做法是安全的。”- http://php.net/manual/en/regexp.reference.escape.php
    除非是双引号或单引号。:/
    要在PHP中转义正则表达式模式变量(或部分变量),请使用preg_quote()函数。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接