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

字符串的开始和结束锚定符

到目前为止,我们已经了解了文字字符字符类。将其中一个放入正则表达式中会告诉正则表达式引擎尝试匹配单个字符。

锚定符是另一种类型。它们根本不匹配任何字符。相反,它们匹配字符之前、之后或之间的位置。它们可用于在特定位置“锚定”正则表达式匹配。脱字符号 ^ 匹配字符串中第一个字符之前的那个位置。将 ^a 应用于 abc 将匹配 a。 ^b 根本不匹配 abc,因为 b 无法在字符串的开始位置之后立即匹配,而字符串的开始位置由 ^ 匹配。请参阅下文了解正则表达式引擎的内部视图。

类似地,$ 匹配字符串中最后一个字符之后的那个位置。 c$abc 中匹配 c,而 a$ 根本不匹配。

仅由锚定符组成的正则表达式只能找到零长度匹配。这可能很有用,但也会造成复杂情况,本教程的结尾附近对此进行了说明。

有用的应用程序

在编程语言中使用正则表达式验证用户输入时,使用锚点非常重要。如果你在 Perl 脚本中使用代码 if ($input =~ m/\d+/) 来查看用户是否输入了一个整数,它将接受输入,即使用户输入 qsdf4ghjk,因为 \d+ 匹配 4。要使用的正确正则表达式是 ^\d+$。因为在匹配 \d+ 之前必须匹配“字符串开头”,并且必须在匹配之后立即匹配“字符串结尾”,所以整个字符串必须由 数字 组成才能匹配 ^\d+$

用户很容易意外地输入一个空格。当 Perl 从文本文件中读取一行时,换行符也会存储在变量中。因此,在验证输入之前,最好修剪前导和尾随 空白^\s+ 匹配前导空白,而 \s+$ 匹配尾随空白。在 Perl 中,你可以使用 $input =~ s/^\s+|\s+$//g。交替和 /g 的巧妙使用允许我们在单行代码中执行此操作。

使用 ^ 和 $ 作为行首和行尾锚点

如果您有一个由多行组成的字符串,如 first line\nsecond line(其中 \n 表示换行符),通常需要处理各行,而不是整个字符串。因此,本教程中讨论的大多数正则表达式引擎可以选择扩展这两个锚点的含义。然后,^ 可以在字符串的开头(在上述字符串中的 f 之前)以及每个换行符之后(在 \ns 之间)匹配。同样,$ 仍然匹配字符串的末尾(在最后一个 e 之后),以及每个换行符之前(在 e\n 之间)。

在像 EditPad Pro 或 GNU Emacs 这样的文本编辑器中,以及像 PowerGREP 这样的正则表达式工具中,插入符号和美元符号始终匹配每行的开头和结尾。这是有道理的,因为这些应用程序旨在处理整个文件,而不是短字符串。在 Rubystd::regex 中,插入符号和美元符号也始终匹配每行的开头和结尾。在 Boost 中,它们默认情况下匹配每行的开头和结尾。使用 ECMAScript 语法时,Boost 允许您使用 regex_constants::no_mod_m 关闭此功能。

在本网站上讨论的所有其他编程语言和库中,您必须显式激活此扩展功能。它传统上称为“多行模式”。在 Perl 中,您可以通过在正则表达式代码后添加 m 来实现,如下所示:m/^regex$/m;。在 .NET 中,当您指定 RegexOptions.Multiline 时,锚点匹配换行符之前和之后,例如在 Regex.Match("string", "regex", RegexOptions.Multiline) 中。

换行符

有关点号的教程页面已经讨论了哪些字符被各种正则表达式风格视为 换行符。当处于多行模式时,这会对锚点产生同样的影响,并且当美元符号匹配最终换行符的末尾之前。锚点处理由单个字符组成的换行符的方式与每个正则表达式风格中的点号相同。

对于锚点,当 CR 和 LF 成对出现并且正则表达式风格将这两个字符都视为换行符时,还需要额外考虑。 DelphiJavaJGsoft 风格 将 CRLF 视为不可分割的一对。 ^ 匹配 CRLF 之后,$ 匹配 CRLF 之前,但都不匹配 CRLF 对的中间。 JavaScriptXPath 将 CRLF 对视为两个换行符。 ^ 匹配 CRLF 的中间和之后,而 $ 匹配 CRLF 的之前和中间。

字符串的永久开头和结尾锚点

\A 仅在字符串开头匹配。同样,\Z 仅在字符串结尾匹配。这两个标记绝不会在换行符处匹配。本教程中讨论的所有正则表达式风格中均为如此,即使您启用了“多行模式”。在 EditPad Pro 和 PowerGREP 中,插入符号和美元符号始终在行首和行尾匹配,\A\Z 仅在整个文件的开头和结尾匹配。

JavaScriptPOSIXXMLXPath 不支持 \A\Z。您只能为此目的使用插入符号和美元符号。

GNU 扩展 使用 \`(反引号)匹配字符串开头,使用 \'(单引号)匹配字符串结尾。

以换行符结尾的字符串

由于 Perl 在从文件中读取一行时返回一个结尾带有换行符的字符串,因此即使关闭了多行模式,Perl 的正则表达式引擎也会在字符串结尾换行符之前的那个位置匹配 $。Perl 还会在字符串的最后匹配 $,无论该字符是否为换行符。因此,^\d+$ 匹配 123,无论主题字符串是 123 还是 123\n

大多数现代正则表达式风格都复制了此行为。其中包括 .NETJavaPCREDelphiPHPPython。此行为与“多行模式”等任何设置无关。

在除 Python 之外的所有这些风格中,\Z 也匹配在最后换行符之前。如果你只想在字符串的绝对末尾匹配,请使用 \z(小写 z 而不是大写 Z)。\A\d+\z 不匹配 123\n\z 匹配在换行符之后,而 简写字符类 不匹配换行符。

在 Python 中,\Z 仅在字符串的末尾匹配。Python 不支持 \z

以多行换行符结尾的字符串

如果字符串以多行换行符结尾且多行模式关闭,则 $ 仅在所有风格中最后一个换行符之前匹配,在这些风格中它可以在最后一个换行符之前匹配。对于 \Z 也是如此,无论多行模式如何。

Boost 是唯一的例外。在 Boost 中,\Z 可以匹配在任意数量的尾随换行符之前以及字符串的末尾。因此,如果主题字符串以三个换行符结尾,则 Boost 的 \Z 有四个位置可以匹配。与所有其他风格一样,Boost 的 \Z 与多行模式无关。Boost 的 $ 仅在关闭多行模式(在 Boost 中默认开启)时在字符串末尾匹配。

查看正则表达式引擎内部

让我们看看当我们尝试将 ^4$ 匹配到 749\n486\n4(其中 \n 表示换行符)的多行模式中时会发生什么。和往常一样,正则表达式引擎从第一个字符开始:7。正则表达式中的第一个标记是 ^。由于此标记是零长度标记,因此引擎不会尝试将其与字符匹配,而是将其与正则表达式引擎迄今为止到达的字符之前的那个位置匹配。^ 确实匹配 7 之前的位置。然后,引擎前进到下一个正则表达式标记:4。由于前一个标记是零长度,因此正则表达式引擎不会前进到字符串中的下一个字符。它仍然停留在 74 是一个文本字符,不匹配 7。正则表达式没有其他排列,因此引擎从第一个正则表达式标记重新开始,在下一个字符:4。这一次,^ 无法匹配在 4 之前的位置。此位置之前是一个字符,并且该字符不是换行符。引擎继续在 9,再次失败。下一次尝试,在 \n,也失败了。同样,\n 之前的位置之前是一个字符 9,并且该字符不是换行符。

然后,正则表达式引擎到达字符串中的第二个 4。由于 ^ 前面有一个换行符,因此它可以在 4 前面的位置匹配。同样,正则表达式引擎前进到下一个正则表达式标记 4,但不会前进字符串中的字符位置。4 匹配 4,引擎前进正则表达式标记和字符串字符。现在,引擎尝试在 8 前面的位置(实际上:前面)匹配 $。美元符号不能在此处匹配,因为该位置后面跟着一个字符,并且该字符不是换行符。

引擎必须再次尝试匹配第一个标记。之前,它在第二个 4 处成功匹配,因此引擎继续在下一个字符 8 处,在此处脱字符不匹配。在 6 和换行符处也是如此。

最后,正则表达式引擎尝试在字符串中的第三个 4 处匹配第一个标记。成功。之后,引擎成功地用 4 匹配 4。当前正则表达式标记前进到 $,当前字符前进到字符串中的最后一个位置:字符串后面的空位。没有需要字符才能匹配的正则表达式标记可以在这里匹配。甚至 否定字符类 也不行。但是,我们尝试匹配美元符号,而强大的美元是一个奇怪的野兽。它长度为零,因此它尝试匹配当前字符前面的位置。这个“字符”是字符串后面的空位并不重要。事实上,美元符号检查当前字符。对于 $ 匹配当前字符前面的位置,它必须是换行符或字符串后面的空位。由于在示例之后就是这种情况,因此美元符号成功匹配。

由于 $ 是正则表达式中的最后一个标记,因此引擎找到了一个成功匹配:字符串中的最后一个 4