快速入门
教程
工具和语言
示例
参考
书籍评论
正则表达式教程
简介
目录
特殊字符
不可打印字符
正则表达式引擎内部
字符类
字符类减法
字符类交集
简写字符类
点号
锚点
单词边界
交替
可选项
重复
分组和捕获
反向引用
反向引用,第 2 部分
命名组
相对反向引用
分支重置组
自由间距和注释
Unicode
模式修饰符
原子分组
占有量词
前瞻和后顾
环视,第 2 部分
将文本保留在匹配项之外
条件
平衡组
递归
子例程
无限递归
递归和量词
递归和捕获
递归和反向引用
递归和回溯
POSIX 方括号表达式
零长度匹配
继续匹配
本网站上的更多内容
简介
正则表达式快速入门
正则表达式教程
替换字符串教程
应用程序和语言
正则表达式示例
正则表达式参考
替换字符串参考
书籍评论
可打印 PDF
关于本网站
RSS 源和博客
RegexBuddy—Better than a regular expression tutorial!

点号匹配(几乎)任何字符

在正则表达式中,点号或句点是最常用的 元字符 之一。不幸的是,它也是最常被误用的元字符。

点号匹配单个字符,而不考虑该字符是什么。唯一的例外是换行符。在本教程中讨论的所有正则表达式风格中,点号默认情况下匹配换行符。

此异常的存在主要出于历史原因。使用正则表达式的第一个工具是基于行的。它们逐行读取文件,并将正则表达式分别应用于每一行。其效果是,使用这些工具时,字符串永远不能包含换行符,因此点永远不能与它们匹配。

现代工具和语言可以将正则表达式应用于非常大的字符串,甚至整个文件。除了 VBScript,此处讨论的所有正则表达式风格都具有使点匹配所有字符(包括换行符)的选项。较早的 JavaScript 实现也没有此选项。它在 ECMAScript 2018 规范中正式添加。

在 PowerGREP 中,勾选标记为“点匹配换行符”的复选框,以使点匹配所有字符。在 EditPad Pro 中,启用“点”或“点匹配换行符”搜索选项。

在 Perl 中,点也匹配换行符的模式称为“单行模式”。这有点不幸,因为很容易将此术语与“多行模式”混淆。多行模式仅影响 锚点,而单行模式仅影响点。您可以通过在正则表达式代码后添加 s 来激活单行模式,如下所示:m/^regex$/s;

其他语言和正则表达式库采用了 Perl 的术语。在使用 .NET 的 Regex 类 时,您可以通过指定 RegexOptions.Singleline 来激活此模式,例如 Regex.Match("string", "regex", RegexOptions.Singleline)

JavaScript(为了与旧浏览器兼容)和 VBScript 中,可以使用 字符类,例如 [\s\S] 来匹配任何字符。此字符匹配空格字符(包括换行符)或非空格字符。由于所有字符都是空格或非空格,因此此字符类匹配任何字符。不要使用 (\s|\S) 这样的交替,这会很慢。当然不要使用 (.|\s),因为空格和制表符既可以被 . 匹配,也可以被 \s 匹配,这会导致灾难性的回溯

在所有 Boost 的正则表达式语法中,点默认匹配换行符。Boost 的 ECMAScript 语法允许您使用 regex_constants::no_mod_m 关闭此功能。

换行符

虽然点在各种正则表达式风格中都得到支持,但它们将哪些字符视为换行符却存在显著差异。所有风格都将换行符 \n 视为换行符。UNIX 文本文件使用单个换行符终止行。本教程中讨论的所有脚本语言都不将任何其他字符视为换行符。即使在通常使用 \r\n 对来换行的 Windows 上,这也不是问题。这是因为这些脚本语言在默认情况下以文本模式读取和写入文件。在 Windows 上运行时,在读取文件时 \r\n 对会自动转换为 \n,而 \n 会自动写入文件为 \r\n

std::regexXML SchemaXPath 也将回车 \r 视为换行符。在此基础上,JavaScript 添加了 Unicode 行分隔符 \u2028 和段落分隔符 \u2029Java 包含这些分隔符,外加拉丁语-1 下一行控制字符 \u0085Boost 将换页符 \f 添加到列表中。只有 DelphiJGsoft 风格 支持所有 Unicode 换行符,并使用垂直制表符完成了组合。

.NET 在将除 \n 之外的字符视为换行符的风格列表中明显缺失。与根植于 UNIX 世界的脚本语言不同,.NET 是一个 Windows 开发框架,不会自动从读取的文本文件中删除回车符。如果您将 Windows 文本文件整体读入字符串,它将包含回车符。如果您在该字符串上使用正则表达式 abc.*,而不设置 RegexOptions.SingleLine,那么它将匹配 abc 以及同一行上紧随其后的所有字符,以及该行末尾的回车符,但不会匹配其后的换行符。

一些风格允许您控制哪些字符应视为换行符。Java 具有 UNIX_LINES 选项,使其仅将 \n 视为换行符。 PCRE 具有允许您在仅 \n、仅 \r\r\n 或所有 Unicode 换行符之间进行选择的选项。

POSIX 系统中,POSIX 区域设置决定哪些字符是换行符。C 区域设置仅将换行符 \n 视为换行符。Unicode 区域设置支持所有 Unicode 换行符。

\N 绝不匹配换行符

Perl 5.12 和 PCRE 8.10 引入了 \N,它与点号一样,匹配任何不是换行符的单个字符。与点号不同,\N 不受“单行模式”影响。(?s)\N. 启用单行模式,然后匹配任何不是换行符的字符,后跟任何字符,无论它是否是换行符。

控制哪些字符被视为换行符的 PCRE 选项对 \N 的影响与它们对点号的影响完全相同。

PHP 5.3.4 和 R 2.14.0 也支持 \N,因为它们的正则表达式支持基于 PCRE 8.10 或更高版本。JGsoft V2 也支持 \N

谨慎使用点号

点号是一个非常强大的正则表达式元字符。它允许你偷懒。输入一个点号,在对有效数据测试正则表达式时,一切匹配正常。问题在于,正则表达式在不应该匹配的情况下也匹配。如果你刚接触正则表达式,其中一些情况一开始可能并不那么明显。

我们用一个简单的例子来说明这一点。假设我们想要匹配 mm/dd/yy 格式的日期,但我们希望让用户选择日期分隔符。快速解决方案是 \d\d.\d\d.\d\d。乍一看似乎很好。它可以很好地匹配 02/12/03 这样的日期。问题是:02512703 也被此正则表达式视为有效日期。在此匹配中,第一个点号匹配 5,第二个匹配 7。显然不是我们想要的。

\d\d[- /.]\d\d[- /.]\d\d 是一个更好的解决方案。此正则表达式允许使用连字符、空格、点号和正斜杠作为日期分隔符。请记住,点号在 字符类 中不是元字符,因此我们不需要用反斜杠对其进行转义。

此正则表达式还远不完美。它将 99/99/99 匹配为有效日期。[01]\d[- /.][0-3]\d[- /.]\d\d 虽然更进一步,但它仍然匹配 19/39/99。您希望正则表达式有多完美取决于您想要用它做什么。如果您要验证用户输入,则它必须完美。如果您要从每次以相同方式生成文件的已知源解析数据文件,那么我们的最后一次尝试可能足以在不出现错误的情况下解析数据。您可以在示例部分找到一个 更好的正则表达式来匹配日期

使用否定字符类而不是点

否定字符类通常比点更合适。否定字符类 教程部分解释了重复运算符 星号和加号,其中更详细地介绍了这一点。但该警告非常重要,因此在此也需要提到它。我们再次通过一个示例来说明。

假设您想匹配一个双引号字符串。听起来很简单。我们可以在双引号之间包含任意数量的任何字符,因此 ".*" 似乎可以很好地完成这项工作。点匹配任何字符,星号允许点重复任意次数,包括零次。如果您对 Put a "string" between double quotes 测试此正则表达式,它将很好地匹配 "string"。现在继续对 Houston, we have a problem with "string one" and "string two". Please respond. 进行测试。

哎呀。正则表达式匹配 "string one" and "string two"。这绝对不是我们的本意。出现这种情况的原因是 星号贪婪的

在日期匹配示例中,我们通过将点替换为字符类来改进正则表达式。在此,我们对否定字符类执行相同的操作。我们对双引号字符串的原始定义有缺陷。我们不希望引号之间有任何数量的任何字符。我们希望引号之间有任何数量的不是双引号或换行符的字符。因此,正确的正则表达式是 "[^"\r\n]*"。如果您的风格支持 简写 \v 来匹配任何换行符,那么 "[^"\v]*" 是更好的解决方案。