正则表达式中应该转义哪些文字字符?

26

我刚刚为适用于php函数preg_match编写了一个正则表达式,其中包含以下部分:

[\w-.]

匹配任何单词字符,以及减号和点。虽然它在preg_match中似乎可以工作,但我尝试将其放入一个叫做Reggy的实用程序中,它会报错"Empty range in char class"。试错告诉我,通过转义减号来解决此问题,将正则表达式变为:

[\w\-.]

由于原始正则表达式在PHP中似乎可以工作,我想知道为什么我应该或不应该转义减号,以及为什么我不需要转义句点,因为句点也是在PHP中具有含义的字符。我的实用程序只是傻瓜吗?它使用了另一种正则表达式方言还是我的正则表达式真的不正确,而我只是幸运地用preg_match逃脱了呢?


有没有不使用 preg_quote 的理由? - Okonomiyaki3000
可能不会。但这不是我提出问题的原因。我试图学习有关正则表达式的新知识,仅使用preg_quote将产生完全相反的效果。 :) 我确实意识到我标记了这个PHP,但我正在寻找适用于任何PCRE实现的答案。 - Pelle
我明白了。那么,我可以建议您查看:https://github.com/php/php-src/blob/a3ca6b09cdf1ed904d3e3a56878c1cf6b1a04d1b/ext/pcre/php_pcre.c - Okonomiyaki3000
虽然它仍然没有“直接”告诉我需要转义的内容和不需要转义的内容,以及为什么需要这样做,但它确实包含了所有关于其行为方式的答案。供参考,官方源代码的镜像:https://github.com/luvit/pcre2/tree/master/src - Pelle
5个回答

67
在许多正则表达式实现中,以下规则适用:
字符类内的元字符包括:
- ^(取反) - -(范围) - ](结束字符类) - \(转义字符)
因此,这些都应该被转义。但是有一些特殊情况:
- 如果在字符类的开头或结尾放置连字符-时,不需要转义 (例如 [abc-] 或 [-abc])。在很多正则表达式实现中,如果连字符-紧接着一个字符范围(例如[a-c-abc])或简写字符类(例如[\w-abc]),也不需要转义。这是你观察到的。 - 如果^不在字符类的开头,则不需要转义:[ ^a]表示除了a之外的任何字符,而[a^]匹配a或^,等同于[\^a]。 - 如果]是字符类中唯一的字符,则不需要转义:[]]匹配字符]。

非常全面的回答,谢谢。关于[]]有一个问题:如果类中只有一个字符,为什么不将其指定为\]?(即不在方括号中) - Pelle
@Pelle,“为什么不”是另一个问题,与此无关。“有多种方法可以做到这一点”是preg的发明者的座右铭;) - Your Common Sense
2
@Pelle,谢谢。确实,你可以(或者应该?)直接使用\]而不是字符类,但是我想提一下许多正则表达式实现允许[]]匹配文字]。甚至您无需转义],因为它只是字符类中的元字符。在外部,只有[需要从两个方括号中转义(但转义]也无妨!)。 - Bart Kiers
用于包装/分隔RegExp的字符必须进行转义,通常为'/'。 - AFA Med
@AFAMed,那是一种语言限制,不是正则表达式本身的特定问题。 - Bart Kiers
显示剩余2条评论

6
[\w.-]
  • .通常表示任何字符,但在[]之间没有特殊含义。
  • []之间的-表示一个范围,除非它被转义或出现在[]的首尾位置。

在字符类(即方括号之间)中,“.” 真的意味着“任何字符”吗? - Pelle
@Pelle,没错。我正在编辑答案。大多数答案都弄错了;-) - bw_üezi

4

虽然在正则表达式中确实有一些需要转义的字符,但你询问的不是正则表达式而是字符类。其中破折号是特殊字符。

你可以将其放在字符类末尾,而不是转义它,[\w.-]


3
全角句号在字符类中失去了其元意。
字符类中的连字号“-”具有特殊含义。如果它不位于方括号的开头或结尾,就必须进行转义。否则它表示一个字符范围(如“A-Z”)。
然而,你触发了另一个特例。“[\w-.]”之所以有效,是因为“\w”并不表示单个字符。因此PCRE不可能创建一个字符范围。“\w”是一组可能不一致的符号,因此没有结束字符可以用来创建“Z到.”的范围。此外,句点“.”会先于“\w”能匹配的第一个ASCII字符“a”出现。因此无法构造范围。这就是为什么你可以不转义使用连字号“-”。

0
如果您正在使用PHP并且需要转义特殊的正则表达式字符,只需使用preg_quote
来自php.net的示例:
<?php
// In this example, preg_quote($word) is used to keep the
// asterisks from having special meaning to the regular
// expression.

$textbody = "This book is *very* difficult to find.";
$word = "*very*";
$textbody = preg_replace ("/" . preg_quote($word, '/') . "/",
                          "<i>" . $word . "</i>",
                          $textbody);
?>

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