查看源代码 re (stdlib v6.2)

此模块包含用于字符串和二进制数据的正则表达式匹配函数。

正则表达式 语法和语义类似于 Perl。

该库的匹配算法基于 PCRE 库,但并非所有 PCRE 库都已接口化,并且该库的某些部分超出了 PCRE 提供的范围。目前使用 PCRE 版本 8.40(发布日期 2017-01-11)。此模块相关的 PCRE 文档部分包含在此处。

注意

Erlang 字符串的字面量语法使用 \\(反斜杠)字符作为转义码。您需要在字面量字符串中(包括代码和 shell 中)使用额外的反斜杠来转义反斜杠,即 "\\\\"<<"\\\\">>

自 Erlang/OTP 27 起,您可以使用 逐字 sigil 来编写字面量字符串。上面的示例可以写成 ~S"\"~B"\"

类 Perl 的正则表达式语法

以下部分包含此模块使用的正则表达式的参考资料。该信息基于 PCRE 文档,其中此模块的行为与 PCRE 库不同的地方会有所更改。

PCRE 正则表达式详细信息

以下部分详细描述了 PCRE 支持的正则表达式的语法和语义。Perl 的正则表达式在其自己的文档中进行了描述,而一般的正则表达式在许多书籍中都有介绍,其中一些书籍有大量的示例。Jeffrey Friedl 的《精通正则表达式》(由 O'Reilly 出版)非常详细地介绍了正则表达式。对 PCRE 正则表达式的描述旨在作为参考资料。

参考资料分为以下几部分

模式开头的特殊项

一些可以传递给 compile/2 的选项也可以通过模式开头的特殊项进行设置。这些选项与 Perl 不兼容,但提供这些选项是为了使那些无法更改处理模式的程序的模式编写者能够访问这些选项。可以出现任意数量的这些项,但它们必须都位于模式字符串的开头,并且字母必须为大写。

UTF 支持

Unicode 支持基本上基于 UTF-8。要使用 Unicode 字符,您可以调用带有选项 unicodecompile/2run/3,或者模式必须以以下特殊序列之一开头

(*UTF8)
(*UTF)

这两个选项的效果相同,输入字符串被解释为 UTF-8。请注意,使用这些指令时,re 函数不会执行列表到 UTF-8 的自动转换。因此,不建议使用这些序列。在运行 compile/2 时,添加选项 unicode 作为替代方案。

一些允许用户提供模式的应用程序可能出于安全原因希望将它们限制为非 UTF 数据。如果在编译时设置了选项 never_utf,则不允许使用 (*UTF) 等等,并且它们的出现会导致错误。

Unicode 属性支持

以下是另一个可以出现在模式开头的特殊序列

(*UCP)

这与设置选项 ucp 的效果相同:它使诸如 \d\w 之类的序列使用 Unicode 属性来确定字符类型,而不是仅通过查找表来识别代码 < 256 的字符。

禁用启动优化

如果模式以 (*NO_START_OPT) 开头,则其效果与在编译时设置选项 no_start_optimize 相同。

换行约定

PCRE 支持五种约定来指示字符串中的换行符:单个 CR(回车)字符、单个 LF(换行)字符、双字符序列 CRLF、以上三个中的任何一个,以及任何 Unicode 换行序列。

也可以通过以下五个序列之一来启动模式字符串来指定换行约定

  • (*CR) - 回车符

  • (*LF) - 换行符

  • (*CRLF) - >回车符后跟换行符

  • (*ANYCRLF) - 以上三个中的任何一个

  • (*ANY) - 所有 Unicode 换行序列

这些会覆盖 compile/2 的默认值和指定的选项。例如,以下模式将约定更改为 CR

(*CR)a.b

此模式匹配 a\nb,因为 LF 不再是换行符。如果存在多个,则使用最后一个。

换行约定会影响脱字符和美元符断言为真的位置。它还会影响未设置 dotall 时点元字符的解释,以及 \N 的行为。但是,它不会影响 \R 转义序列匹配的内容。默认情况下,为了与 Perl 兼容,这是任何 Unicode 换行序列。但是,可以更改此设置;请参阅 换行序列 部分中对 \R 的描述。\R 设置的更改可以与换行约定的更改结合使用。

设置匹配和递归限制

run/3 的调用者可以限制内部 match() 函数的调用次数和递归调用的最大深度。提供这些功能是为了捕获由具有巨大匹配树的模式(一个典型的例子是具有嵌套无限重复的模式)引发的失控匹配,并通过过多递归来避免耗尽系统堆栈。当达到这些限制之一时,pcre_exec() 会给出错误返回。限制也可以通过以下形式的模式开头的项来设置

(*LIMIT_MATCH=d)
(*LIMIT_RECURSION=d)

这里的 d 是任何十进制数字。但是,设置的值必须小于 run/3 的调用者设置的值,才能生效。也就是说,模式编写者可以降低程序员设置的限制,但不能提高它。如果对这些限制中的一个进行了多次设置,则使用较低的值。

在 Erlang VM 中,这两个限制的默认值为 10,000,000。请注意,递归限制不会影响 VM 的堆栈深度,因为 PCRE for Erlang 的编译方式使得匹配函数永远不会在 C 堆栈上进行递归。

请注意,LIMIT_MATCHLIMIT_RECURSION 只能减小调用者设置的限制值,而不能增加它们。

字符和元字符

正则表达式是一种模式,从左到右与目标字符串进行匹配。模式中的大多数字符代表它们自身,并匹配目标字符串中的相应字符。作为一个简单的示例,以下模式匹配与自身相同的目标字符串的一部分

The quick brown fox

当指定不区分大小写的匹配(选项 caseless)时,字母的匹配与大小写无关。

正则表达式的能力来自于在模式中包含替代项和重复项的能力。这些通过使用元字符编码在模式中,这些元字符不代表它们自身,而是以某种特殊方式解释。

存在两组元字符:一组是在模式中除方括号内之外的任何位置识别的元字符,另一组是在方括号内识别的元字符。在方括号之外,元字符如下

  • \ - 通用转义字符,具有多种用途

  • ^ - 断言字符串的开头(或多行模式下的行的开头)

  • $ - 断言字符串的结尾(或多行模式下的行的结尾)

  • . - (默认情况下)匹配除换行符之外的任何字符

  • [ - 启动字符类定义

  • | - 启动替代分支

  • ( - 启动子模式

  • ) - 结束子模式

  • ? - 扩展 ( 的含义,也是 0 或 1 量词,也是量词最小化器

  • * - 0 个或多个量词

  • + - 1 个或多个量词,也是“占有量词”

  • { - 启动最小/最大量词

方括号内的模式部分称为“字符类”。以下是字符类中唯一的元字符

  • \ - 通用转义字符

  • ^ - 否定类,但仅当它是第一个字符时

  • - - 指示字符范围

  • [ - Posix 字符类(仅当后面跟 Posix 语法时)

  • ] - 终止字符类

以下部分描述了每个元字符的使用。

反斜杠

反斜杠字符有很多用途。首先,如果它后面跟一个非数字或字母的字符,它将消除字符可能具有的任何特殊含义。这种将反斜杠用作转义字符的用法适用于字符类内部和外部。

例如,如果您想匹配一个 "_" 字符,您可以在模式中写入 \_。如果以下字符将被解释为元字符,则此转义操作将生效,因此始终可以安全地在非字母数字字符之前加上反斜杠,以指定它代表自身。特别是,如果您想匹配一个反斜杠,请写入 \\

unicode 模式下,只有 ASCII 数字和字母在反斜杠后才具有任何特殊含义。所有其他字符(特别是代码点 > 127 的字符)都将被视为字面量。

如果使用 extended 选项编译模式,则模式中的空白字符(字符类中的空白字符除外)以及字符类外部的 # 符号到下一个换行符之间的字符都将被忽略。可以使用转义反斜杠来包含空白字符或 # 字符作为模式的一部分。

要删除字符序列的特殊含义,请将其放在 \Q\E 之间。这与 Perl 不同,在 PCRE 中,$@\Q...\E 序列中被视为字面量,而在 Perl 中,$@ 会导致变量插值。请注意以下示例

Pattern            PCRE matches   Perl matches

\Qabc$xyz\E        abc$xyz        abc followed by the contents of $xyz
\Qabc\$xyz\E       abc\$xyz       abc\$xyz
\Qabc\E\$\Qxyz\E   abc$xyz        abc$xyz

\Q...\E 序列在字符类内部和外部都被识别。孤立的、前面没有 \Q\E 将被忽略。如果在模式中稍后 \Q 后面没有 \E,则字面量解释将继续到模式的末尾(也就是说,在末尾假定存在 \E)。如果孤立的 \Q 位于字符类内部,则会导致错误,因为字符类未终止。

非打印字符

反斜杠的第二种用途是以可见的方式在模式中编码非打印字符。除了终止模式的二进制零之外,对非打印字符的出现没有任何限制。当通过文本编辑准备模式时,通常使用以下转义序列之一比使用它表示的二进制字符更容易

  • \a - 响铃,即 BEL 字符(十六进制 07)

  • \cx - “Control-x”,其中 x 是任何 ASCII 字符

  • \e - Escape(十六进制 1B)

  • \f - 换页(十六进制 0C)

  • \n - 换行(十六进制 0A)

  • \r - 回车(十六进制 0D)

  • \t - 制表符(十六进制 09)

  • \0dd - 八进制代码为 0dd 的字符

  • \ddd - 八进制代码为 ddd 的字符,或后向引用

  • \o{ddd..} - 八进制代码为 ddd.. 的字符

  • \xhh - 十六进制代码为 hh 的字符

  • \x{hhh..} - 十六进制代码为 hhh.. 的字符

注意

请注意,\0dd 始终是八进制代码,并且 \8\9 是字面字符 “8” 和 “9”。

\cx 对 ASCII 字符的精确效果如下:如果 x 是小写字母,则将其转换为大写字母。然后反转字符的第 6 位(十六进制 40)。因此,\cA\cZ 变为十六进制 01 到十六进制 1A(A 是 41,Z 是 5A),但 \c{ 变为十六进制 3B({ 是 7B),而 \c; 变为十六进制 7B(; 是 3B)。如果 \c 后面的数据项(字节或 16 位值)的值 > 127,则会发生编译时错误。这会在所有模式下锁定非 ASCII 字符。

\c 功能最初设计用于 ASCII 字符,但随着 Unicode 的扩展,它比以前更没用了。

\0 之后,最多读取两个额外的八进制数字。如果少于两个数字,则仅使用存在的数字。因此,序列 \0\x\015 指定两个二进制零,后跟一个 CR 字符(代码值 13)。如果紧随其后的模式字符本身是八进制数字,请确保在初始零之后提供两个数字。

转义 \o 后面必须跟一个用大括号括起来的八进制数字序列。如果不是这种情况,则会发生错误。此转义是 Perl 最近添加的;它提供了一种将字符代码点指定为大于 0777 的八进制数的方法,并且还允许明确指定八进制数和后向引用。

为了更清晰和明确,最好避免在 \ 后面跟一个大于零的数字。相反,请使用 \o{}\x{} 来指定字符编号,并使用 \g{} 来指定后向引用。以下段落描述了旧的、不明确的语法。

反斜杠后跟非 0 数字的处理很复杂,Perl 在最近的版本中已更改,导致 PCRE 也进行了更改。在字符类外部,PCRE 将该数字和任何后续数字读取为十进制数。如果该数字 < 8,或者表达式中至少有那么多先前捕获的左括号,则整个序列被视为后向引用。有关其工作原理的描述将在后面提供,在讨论带括号的子模式之后。

在字符类内部,或者如果 \ 后面的十进制数 > 7 并且没有那么多捕获的子模式,则 PCRE 将 \8\9 处理为字面字符 “8” 和 “9”,否则会重新读取反斜杠后面的最多三个八进制数字,并使用它们生成数据字符。任何后续数字都代表它们自身。例如

  • \040 - 编写 ASCII 空格的另一种方式

  • \40 - 相同,前提是有 < 40 个先前的捕获子模式

  • \7 - 始终是后向引用

  • \11 - 可以是后向引用,也可以是编写制表符的另一种方式

  • \011 - 始终是制表符

  • \0113 - 制表符,后跟字符 “3”

  • \113 - 可以是后向引用,否则是八进制代码为 113 的字符

  • \377 - 可以是后向引用,否则值为 255(十进制)

  • \81 - 要么是后向引用,要么是两个字符 “8” 和 “1”

请注意,使用此语法指定的 >= 100 的八进制值不能以 0 开头,因为最多读取三个八进制数字。

默认情况下,在 \x(后跟 {)之后,会读取零到两个十六进制数字(字母可以是大小写)。任意数量的十六进制数字可能会出现在 \x{} 之间。如果 \x{} 之间出现非十六进制数字的字符,或者没有终止 },则会发生错误。

可以使用 \x 的两种语法中的任何一种来定义值小于 256 的字符。它们的处理方式没有区别。例如,\xdc\x{dc} 完全相同。

对字符值的约束

使用八进制或十六进制数字指定的字符受限于某些值,如下所示

  • 8 位非 UTF 模式 - < 0x100

  • 8 位 UTF-8 模式 - < 0x10ffff 且为有效的代码点

无效的 Unicode 代码点是 0xd800 到 0xdfff 范围(所谓的“代理”代码点)和 0xffef。

字符类中的转义序列

定义单个字符值的所有序列都可以在字符类内部和外部使用。此外,在字符类内部,\b 被解释为退格字符(十六进制 08)。

字符类中不允许使用 \N\B\R\X 在字符类内部不是特殊的。像其他无法识别的转义序列一样,它们被视为字面字符 “B”、“R” 和 “X”。在字符类外部,这些序列具有不同的含义。

不支持的转义序列

在 Perl 中,序列 \l\L\u\U 被其字符串处理程序识别,并用于修改以下字符的大小写。PCRE 不支持这些转义序列。

绝对和相对后向引用

序列 \g 后跟一个无符号或负数,可以选择用大括号括起来,是绝对或相对后向引用。命名的后向引用可以编码为 \g{name}。后向引用将在稍后讨论,在讨论带括号的子模式之后。

绝对和相对子例程调用

为了与 Oniguruma 兼容,非 Perl 语法 \g 后跟一个名称或一个数字,用尖括号或单引号括起来,是引用子模式作为“子例程”的替代语法。详细信息将在稍后讨论。请注意,\g{...}(Perl 语法)和 \g<...>(Oniguruma 语法)不是同义的。前者是后向引用,后者是子例程调用。

通用字符类型

反斜杠的另一种用途是指定通用字符类型

  • \d - 任何十进制数字

  • \D - 任何不是十进制数字的字符

  • \h - 任何水平空白字符

  • \H - 任何不是水平空白字符的字符

  • \s - 任何空白字符

  • \S - 任何不是空白字符的字符

  • \v - 任何垂直空白字符

  • \V - 任何不是垂直空白字符的字符

  • \w - 任何“单词”字符

  • \W - 任何“非单词”字符

还有一个单独的序列 \N,它匹配非换行字符。当未设置 dotall 时,它与 “.” 元字符相同。Perl 也使用 \N 按名称匹配字符,但 PCRE 不支持此功能。

每对小写和大写转义序列将完整字符集划分为两个不相交的集合。任何给定的字符都恰好匹配每对序列中的一个。这些序列可以出现在字符类内部和外部。它们各自匹配一个相应类型的字符。如果当前匹配点位于主题字符串的末尾,则所有匹配都将失败,因为没有字符可以匹配。

为了与 Perl 兼容,\s 过去不匹配 VT 字符(代码 11),这使得它与 POSIX “space” 类不同。然而,Perl 在 5.18 版本中添加了 VT,而 PCRE 在 8.34 版本中也随之添加。现在,默认的 \s 字符是 HT (9)、LF (10)、VT (11)、FF (12)、CR (13) 和空格 (32),它们在 “C” 语言环境中被定义为空白字符。如果正在进行特定于语言环境的匹配,则此列表可能会有所不同。例如,在某些语言环境中,“不间断空格”字符 (\xA0) 被识别为空白字符,而在其他语言环境中则不识别 VT 字符。

“单词”字符是下划线或任何字母或数字字符。默认情况下,字母和数字的定义由 PCRE 低值字符表控制,在 Erlang 的情况下(且没有 unicode 选项),是 ISO Latin-1 字符集。

默认情况下,在 unicode 模式下,值大于 255 的字符,即 ISO Latin-1 字符集之外的所有字符,永远不匹配 \d\s\w,并且始终匹配 \D\S\W。这些序列保留了在支持 UTF 之前使用的原始含义,主要是出于效率原因。但是,如果设置了 ucp 选项,则行为会发生变化,以便使用 Unicode 属性来确定字符类型,如下所示:

  • \d - 任何匹配 \p{Nd} 的字符(十进制数字)

  • \s - 任何匹配 \p{Z}\h\v 的字符

  • \w - 任何匹配 \p{L}\p{N} 的字符,加上下划线

大写转义序列匹配字符的反向集合。请注意,\d 仅匹配十进制数字,而 \w 匹配任何 Unicode 数字、任何 Unicode 字母和下划线。另请注意,ucp 会影响 \b\B,因为它们是根据 \w\W 定义的。设置 ucp 后,匹配这些序列的速度会明显变慢。

序列 \h\H\v\V 是在 Perl 5.10 版本中添加的功能。与默认情况下仅匹配 ASCII 字符的其他序列相比,这些序列始终匹配某些高值代码点,无论是否设置了 ucp

以下是水平空格字符:

  • U+0009 - 水平制表符 (HT)

  • U+0020 - 空格

  • U+00A0 - 不间断空格

  • U+1680 - 欧甘空格标记

  • U+180E - 蒙古语元音分隔符

  • U+2000 - 四分空格

  • U+2001 - 全角空格

  • U+2002 - 半角空格

  • U+2003 - 全角空格

  • U+2004 - 三分之一全角空格

  • U+2005 - 四分之一全角空格

  • U+2006 - 六分之一全角空格

  • U+2007 - 数字空格

  • U+2008 - 标点空格

  • U+2009 - 窄空格

  • U+200A - 极窄空格

  • U+202F - 窄不间断空格

  • U+205F - 中等数学空格

  • U+3000 - 表意空格

以下是垂直空格字符:

  • U+000A - 换行符 (LF)

  • U+000B - 垂直制表符 (VT)

  • U+000C - 换页符 (FF)

  • U+000D - 回车符 (CR)

  • U+0085 - 下一行 (NEL)

  • U+2028 - 行分隔符

  • U+2029 - 段落分隔符

在 8 位非 UTF-8 模式下,只有代码点 < 256 的字符才相关。

换行序列

默认情况下,在字符类外部,转义序列 \R 匹配任何 Unicode 换行序列。在非 UTF-8 模式下,\R 等效于以下内容:

(?>\r\n|\n|\x0b|\f|\r|\x85)

这是“原子组”的一个示例,详细信息将在下文提供。

这个特定的组匹配双字符序列 CR 后跟 LF,或者单字符 LF(换行符,U+000A)、VT(垂直制表符,U+000B)、FF(换页符,U+000C)、CR(回车符,U+000D)或 NEL(下一行,U+0085)中的一个。双字符序列被视为不能拆分的单个单元。

在 Unicode 模式下,添加了两个代码点 > 255 的字符:LS(行分隔符,U+2028)和 PS(段落分隔符,U+2029)。无需 Unicode 字符属性支持即可识别这些字符。

通过在编译时或匹配模式时设置 bsr_anycrlf 选项,可以将 \R 限制为仅匹配 CR、LF 或 CRLF(而不是完整的 Unicode 行尾集合)。(BSR 是 “反斜杠 R” 的缩写。)这可以在构建 PCRE 时设置为默认值;如果是这样,可以通过 bsr_unicode 选项请求其他行为。这些设置也可以通过以以下序列之一开始模式字符串来指定:

  • (*BSR_ANYCRLF) - 仅 CR、LF 或 CRLF

  • (*BSR_UNICODE) - 任何 Unicode 换行序列

这些会覆盖默认值和编译函数指定的选项,但它们本身可以被匹配函数指定的选项覆盖。请注意,这些特殊设置(与 Perl 不兼容)仅在模式的开头识别,并且必须为大写。如果存在多个设置,则使用最后一个设置。它们可以与换行约定更改相结合;例如,模式可以以以下开头:

(*ANY)(*BSR_ANYCRLF)

它们也可以与 (*UTF8)、(*UTF) 或 (*UCP) 特殊序列相结合。在字符类内部,\R 被视为无法识别的转义序列,因此默认情况下匹配字母 “R”。

Unicode 字符属性

另外还有三个转义序列可用于匹配具有特定属性的字符。在 8 位非 UTF-8 模式下,这些序列仅限于测试代码点 < 256 的字符,但它们在这种模式下确实有效。以下是额外的转义序列:

  • \p{_xx_} - 具有属性 _xx_ 的字符

  • \P{_xx_} - 没有属性 _xx_ 的字符

  • \X - Unicode 扩展字形簇

上面用 _xx_ 表示的属性名称仅限于 Unicode 脚本名称、通用类别属性、“Any”(匹配任何字符,包括换行符)以及一些特殊的 PCRE 属性(在下一节中描述)。PCRE 当前不支持其他 Perl 属性,例如 “InMusicalSymbols”。请注意,\P{Any} 不匹配任何字符,并且始终会导致匹配失败。

Unicode 字符集被定义为属于某些脚本。可以使用脚本名称匹配其中一个集合中的字符,例如:

\p{Greek} \P{Han}

不属于已识别脚本的字符被归类为 “Common”。以下是当前脚本列表:

  • 阿拉伯语
  • 亚美尼亚语
  • 阿维斯陀语
  • 巴厘语
  • 巴姆姆语
  • 巴萨瓦语
  • 巴塔克语
  • 孟加拉语
  • 注音符号
  • 盲文
  • 布吉语
  • 布希德语
  • 加拿大原住民语
  • 卡里亚语
  • 高加索阿尔巴尼亚语
  • 查克玛语
  • 占语
  • 切罗基语
  • 通用
  • 科普特语
  • 楔形文字
  • 塞浦路斯语
  • 西里尔语
  • 迪赛雷特语
  • 天城文
  • 杜普洛扬语
  • 埃及象形文字
  • 埃尔巴桑语
  • 埃塞俄比亚语
  • 格鲁吉亚语
  • 格拉哥里语
  • 哥特语
  • 格兰塔语
  • 希腊语
  • 古吉拉特语
  • 古木基语
  • 汉字
  • 韩文
  • 哈努诺语
  • 希伯来语
  • 平假名
  • 帝国阿拉米语
  • 继承
  • 碑刻帕拉维语
  • 碑刻安息语
  • 爪哇语
  • 凯提语
  • 卡纳达语
  • 片假名
  • 卡亚利语
  • 佉卢文
  • 高棉语
  • 科基语
  • 库达瓦迪语
  • 老挝语
  • 拉丁语
  • 雷布查语
  • 林布语
  • 线形文字 A
  • 线形文字 B
  • 傈僳语
  • 吕基亚语
  • 吕底亚语
  • 玛哈加尼语
  • 马拉雅拉姆语
  • 曼达语
  • 摩尼教语
  • 梅泰语
  • 门德基卡库语
  • 麦罗埃草书
  • 麦罗埃象形文字
  • 苗语
  • 莫迪语
  • 蒙古语
  • 姆罗语
  • 缅甸语
  • 纳巴泰语
  • 新傣仂语
  • 恩科语
  • 欧甘语
  • 奥尔齐基语
  • 古意大利语
  • 古北阿拉伯语
  • 古彼尔姆语
  • 古波斯语
  • 奥里亚语
  • 古南阿拉伯语
  • 古突厥语
  • 奥斯曼亚语
  • 帕霍苗语
  • 帕尔米拉语
  • 包钦豪语
  • 八思巴字
  • 腓尼基语
  • 诗篇巴列维语
  • 拉让语
  • 卢恩语
  • 撒玛利亚语
  • 索拉什特拉语
  • 夏拉达语
  • 萧伯纳语
  • 悉昙语
  • 僧伽罗语
  • 索拉松朋语
  • 巽他语
  • 锡尔赫蒂文
  • 叙利亚语
  • 他加禄语
  • 塔格巴努亚语
  • 傣仂语
  • 兰纳语
  • 傣担语
  • 塔克里语
  • 泰米尔语
  • 泰卢固语
  • 塔纳语
  • 泰语
  • 藏语
  • 提非纳语
  • 提尔胡塔语
  • 乌加里特语
  • 瓦伊语
  • 瓦朗齐蒂语
  • 彝语

每个字符都具有一个 Unicode 通用类别属性,由两个字母的缩写指定。为了与 Perl 兼容,可以通过在左大括号和属性名称之间包含一个插入符号来指定否定。例如,\p{^Lu}\P{Lu} 相同。

如果使用 \p\P 仅指定一个字母,则它包括所有以此字母开头的通用类别属性。在这种情况下,在没有否定的情况下,转义序列中的大括号是可选的。以下两个示例具有相同的效果:

\p{L}
\pL

支持以下通用类别属性代码:

  • C - 其他

  • Cc - 控制

  • Cf - 格式

  • Cn - 未分配

  • Co - 私人使用

  • Cs - 代理

  • L - 字母

  • Ll - 小写字母

  • Lm - 修饰字母

  • Lo - 其他字母

  • Lt - 首字母大写字母

  • Lu - 大写字母

  • M - 标记

  • Mc - 间距标记

  • Me - 封闭标记

  • Mn - 非间距标记

  • N - 数字

  • Nd - 十进制数字

  • Nl - 字母数字

  • No - 其他数字

  • P - 标点

  • Pc - 连接符标点

  • Pd - 破折号标点

  • Pe - 结束标点

  • Pf - 末尾标点

  • Pi - 初始标点

  • Po - 其他标点

  • Ps - 开标点

  • S - 符号

  • Sc - 货币符号

  • Sk - 修饰符符号

  • Sm - 数学符号

  • So - 其他符号

  • Z - 分隔符

  • Zl - 行分隔符

  • Zp - 段落分隔符

  • Zs - 空格分隔符

还支持特殊的属性 L&。它匹配具有 Lu、Ll 或 Lt 属性的字符,即不归类为修饰符或“其他”的字母。

Cs (代理) 属性仅适用于 U+D800 到 U+DFFF 范围内的字符。此类字符在 Unicode 字符串中无效,因此无法通过 PCRE 进行测试。Perl 不支持 Cs 属性。

PCRE 不支持 Perl 支持的属性名称的长同义词(例如 \p{Letter})。不允许在任何这些属性前加上“Is”。

Unicode 表中没有字符具有 Cn(未分配)属性。对于不在 Unicode 表中的任何代码点,都假设具有此属性。

指定不区分大小写的匹配不会影响这些转义序列。例如,\p{Lu} 始终只匹配大写字母。这与当前版本的 Perl 的行为不同。

按 Unicode 属性匹配字符的速度不快,因为 PCRE 必须执行多阶段表查找才能找到字符属性。这就是为什么默认情况下 PCRE 中像 \d\w 这样的传统转义序列不使用 Unicode 属性的原因。但是,您可以通过设置选项 ucp 或通过以 (*UCP) 开头模式来使它们这样做。

扩展字形簇

\X 转义匹配形成“扩展字形簇”的任意数量的 Unicode 字符,并将该序列视为原子组(见下文)。在 8.31 及之前的版本中,PCRE 匹配的是较早、较简单的定义,等效于 (?>\PM\pM*)。也就是说,它匹配一个没有“标记”属性的字符,后跟零个或多个具有“标记”属性的字符。具有“标记”属性的字符通常是不影响前一个字符的非间距重音符。

Unicode 中扩展了这个简单的定义,通过为每个字符赋予一个字形断开属性,并创建使用这些属性来定义扩展字形簇边界的规则,以包含更复杂的组合字符。在 PCRE 8.31 之后的版本中,\X 匹配这些簇中的一个。

\X 始终匹配至少一个字符。然后,它根据以下结束簇的规则决定是否添加更多字符

  1. 在主题字符串的末尾结束。
  2. 不要在 CR 和 LF 之间结束;否则,在任何控制字符之后结束。
  3. 不要中断韩文(一种朝鲜文字)音节序列。韩文字符有五种类型:L、V、T、LV 和 LVT。一个 L 字符后面可以跟一个 L、V、LV 或 LVT 字符。一个 LV 或 V 字符后面可以跟一个 V 或 T 字符。一个 LVT 或 T 字符后面只能跟一个 T 字符。
  4. 不要在扩展字符或间距标记之前结束。具有“标记”属性的字符始终具有“扩展”字形断开属性。
  5. 不要在预置字符后结束。
  6. 否则,结束簇。

PCRE 附加属性

除了前面描述的标准 Unicode 属性外,PCRE 还支持另外四个属性,这些属性使得可以将传统的转义序列(例如 \w\s)转换为使用 Unicode 属性。当传递 ucp 选项时,PCRE 在内部使用这些非标准、非 Perl 属性。但是,它们也可以显式使用。属性如下:

  • Xan - 任何字母数字字符。匹配具有 L(字母)或 N(数字)属性的字符。

  • Xps - 任何 Posix 空格字符。匹配制表符、换行符、垂直制表符、换页符、回车符以及任何具有 Z(分隔符)属性的其他字符。

  • Xsp - 任何 Perl 空格字符。匹配与 Xps 相同的内容,但垂直制表符除外。

  • Xwd - 任何 Perl“单词”字符。匹配与 Xan 相同的字符,加上下划线。

Perl 和 POSIX 空格现在相同。Perl 在 5.18 版本中将 VT 添加到其空格字符集中,PCRE 在 8.34 版本中进行了更改。

Xan 匹配具有 L(字母)或 N(数字)属性的字符。Xps 匹配制表符、换行符、垂直制表符、换页符或回车符以及任何具有 Z(分隔符)属性的其他字符。Xsp 与 Xps 相同;它曾经排除垂直制表符以与 Perl 兼容,但 Perl 发生了变化,因此 PCRE 在 8.34 版本中也进行了更改。Xwd 匹配与 Xan 相同的字符,加上下划线。

还有另一个非标准属性 Xuc,它匹配 C++ 和其他编程语言中可以用通用字符名称表示的任何字符。这些字符是 $@`(重音符)和所有 Unicode 代码点 >= U+00A0 的字符,代理字符 U+D800 到 U+DFFF 除外。请注意,大多数基本 (ASCII) 字符都被排除在外。(通用字符名称的形式为 \uHHHH\UHHHHHHHH,其中 H 是十六进制数字。请注意,Xuc 属性不匹配这些序列,而是匹配它们表示的字符。)

重置匹配开始位置

转义序列 \K 导致任何先前匹配的字符不包含在最终匹配的序列中。例如,以下模式匹配“foobar”,但报告它匹配了“bar”

foo\Kbar

此功能类似于后向断言(如下所述)。但是,在这种情况下,实际匹配之前的被测部分不必是固定长度的,就像后向断言一样。\K 的使用不会干扰捕获子字符串的设置。例如,当以下模式匹配“foobar”时,第一个子字符串仍然设置为“foo”

(foo)\Kbar

Perl 文档说明在断言中使用 \K 是“未明确定义的”。在 PCRE 中,\K 在其出现在正向断言内部时会起作用,但在负向断言中会被忽略。请注意,当模式(例如 (?=ab\K))匹配时,报告的匹配开始位置可能大于匹配结束位置。

简单断言

反斜杠的最后一种用法是用于某些简单的断言。断言指定必须在匹配中的特定点满足的条件,而不从被测字符串中使用任何字符。下面描述了使用子模式进行更复杂断言的方法。以下是带有反斜杠的断言

  • \b - 在单词边界匹配。

  • \B - 不在单词边界时匹配。

  • \A - 在被测字符串的开头匹配。

  • \Z - 在被测字符串的末尾以及被测字符串末尾的换行符之前匹配。

  • \z - 仅在被测字符串的末尾匹配。

  • \G - 在被测字符串中的第一个匹配位置匹配。

在字符类内部,\b 具有不同的含义;它匹配退格字符。如果这些断言中的任何其他断言出现在字符类中,则默认情况下它会匹配相应的文字字符(例如,\B 匹配字母 B)。

单词边界是被测字符串中的一个位置,其中当前字符和前一个字符都不匹配 \w\W(也就是说,一个匹配 \w,另一个匹配 \W),或者如果第一个或最后一个字符分别匹配 \w,则为字符串的开头或结尾。在 UTF 模式下,可以通过设置选项 ucp 来更改 \w\W 的含义。执行此操作时,还会影响 \b\B。PCRE 和 Perl 没有单独的“单词开头”或“单词结尾”元序列。但是,\b 后面跟着的内容通常会确定它是哪个。例如,片段 \ba 匹配单词开头的“a”。

\A\Z\z 断言与传统的脱字符号和美元符号(在下一节中描述)不同,它们始终只匹配被测字符串的开头和结尾,无论设置什么选项。因此,它们与多行模式无关。这三个断言不受选项 notbolnoteol 的影响,这些选项仅影响脱字符号和美元符号元字符的行为。但是,如果 run/3 的参数 startoffset 非零,表示匹配要从被测字符串开头以外的点开始,则 \A 永远无法匹配。\Z\z 之间的区别在于 \Z 在字符串末尾的换行符之前和字符串末尾都匹配,而 \z 仅在末尾匹配。

只有当当前匹配位置位于 run/3 的参数 startoffset 指定的匹配起始点时,\G 断言才为真。当 startoffset 的值非零时,它与 \A 不同。通过使用适当的参数多次调用 run/3,您可以模仿 Perl 选项 /g,并且 \G 可以在这种类型的实现中发挥作用。

但是,请注意,PCRE 对 \G 的解释(作为当前匹配的开始)与 Perl 的解释略有不同,Perl 将其定义为上一次匹配的结束。在 Perl 中,当上一次匹配的字符串为空时,它们可能会不同。由于 PCRE 一次只进行一个匹配,因此无法重现此行为。

如果模式的所有备选项都以 \G 开头,则该表达式将锚定到起始匹配位置,并且在已编译的正则表达式中设置“anchored”标志。

脱字符和美元符

脱字符 (^) 和美元符 ($) 是零宽度断言。也就是说,它们测试特定条件是否为真,而不会从目标字符串中消耗任何字符。

在字符类外部,在默认匹配模式下,脱字符是一个断言,只有当当前匹配点位于目标字符串的开头时才为真。如果 run/3 的参数 startoffset 非零,则如果未设置 multiline 选项,脱字符永远不会匹配。在字符类内部,脱字符具有完全不同的含义(见下文)。

如果涉及到一些备选项,则脱字符不必是模式的第一个字符,但如果模式要匹配该分支,它必须是其中出现的每个备选项中的第一个。如果所有可能的备选项都以脱字符开头,也就是说,如果模式被限制为仅在目标的开头匹配,则称其为“锚定”模式。(还有其他构造可以导致模式被锚定。)

美元符是一个断言,只有当当前匹配点位于目标字符串的末尾,或者紧挨着字符串末尾的换行符(默认情况下)时才为真。请注意,它不匹配换行符。如果涉及到一些备选项,则美元符不必是模式的最后一个字符,但它必须是其中出现的任何分支中的最后一项。美元符在字符类中没有特殊含义。

可以通过在编译时设置 dollar_endonly 选项来更改美元符的含义,使其仅在字符串的末尾匹配。这不会影响 \Z 断言。

如果设置了 multiline 选项,则脱字符和美元符的含义会发生变化。在这种情况下,脱字符匹配内部换行符之后和目标字符串的开头。它不匹配结束字符串的换行符。当设置 multiline 时,美元符匹配字符串中的任何换行符之前和字符串的末尾。当换行符指定为双字符序列 CRLF 时,单独的 CR 和 LF 字符不表示换行符。

例如,模式 /^abc$/ 在多行模式下匹配目标字符串 "def\nabc"(其中 \n 表示换行符),但在其他情况下则不匹配。因此,由于所有分支都以 ^ 开头而在单行模式下被锚定的模式在多行模式下不会被锚定,并且当 run/3 的参数 startoffset 非零时,脱字符是可能匹配的。dollar_endonly 选项在设置 multiline 时会被忽略。

请注意,序列 \A\Z\z 可用于在两种模式下都匹配目标的开头和结尾。如果模式的所有分支都以 \A 开头,则它始终被锚定,无论是否设置 multiline

句点(点)和 \N

在字符类外部,模式中的点匹配目标字符串中的任何字符,但(默认情况下)表示行尾的字符除外。

当行尾定义为单个字符时,点永远不会匹配该字符。当使用双字符序列 CRLF 时,如果 CR 紧跟 LF,则点不匹配 CR,否则它匹配所有字符(包括单独的 CR 和 LF)。当识别任何 Unicode 行尾时,点不匹配 CR、LF 或任何其他行尾字符。

可以更改点关于换行符的行为。如果设置了 dotall 选项,则点将匹配任何字符,没有任何例外。如果目标字符串中存在双字符序列 CRLF,则需要两个点来匹配它。

点的处理完全独立于脱字符和美元符的处理,唯一的联系是它们都涉及换行符。点在字符类中没有特殊含义。

转义序列 \N 的行为类似于点,但它不受 PCRE_DOTALL 选项的影响。也就是说,它匹配任何字符,但表示行尾的字符除外。Perl 也使用 \N 按名称匹配字符,但 PCRE 不支持此功能。

匹配单个数据单元

在字符类外部,转义序列 \C 匹配任何数据单元,无论是否设置了 UTF 模式。一个数据单元是一个字节。与点不同,\C 始终匹配行尾字符。Perl 中提供了此功能,以在 UTF-8 模式下匹配单个字节,但尚不清楚如何有效地使用它。由于 \C 将字符分解为单个数据单元,因此在 UTF 模式下用 \C 匹配一个单元意味着剩余的字符串可以以格式错误的 UTF 字符开头。由于 PCRE 假设它处理有效的 UTF 字符串,因此会产生未定义的结果。

PCRE 不允许 \C 出现在 UTF 模式下的后向断言中(如下所述),因为这将使其无法计算后向断言的长度。

最好避免使用 \C 转义序列。但是,避免格式错误的 UTF 字符问题的一种使用方法是使用前瞻来检查下一个字符的长度,如下面的模式所示,该模式可以与 UTF-8 字符串一起使用(忽略空格和换行符)

(?| (?=[\x00-\x7f])(\C) |
    (?=[\x80-\x{7ff}])(\C)(\C) |
    (?=[\x{800}-\x{ffff}])(\C)(\C)(\C) |
    (?=[\x{10000}-\x{1fffff}])(\C)(\C)(\C)(\C))

以 (?| 开头的组将重置每个备选项中的捕获括号编号(请参阅 重复的子模式编号 部分)。每个分支开头的断言分别检查下一个 UTF-8 字符的值,这些值使用 1、2、3 或 4 个字节进行编码。然后,字符的各个字节由相应数量的组捕获。

方括号和字符类

左方括号引入一个字符类,由右方括号终止。默认情况下,单独的右方括号不是特殊的。但是,如果设置了 PCRE_JAVASCRIPT_COMPAT 选项,则单独的右方括号会导致编译时错误。如果需要将右方括号作为类的成员,则它必须是类中的第一个数据字符(在初始脱字符之后,如果存在)或使用反斜杠转义。

字符类匹配目标中的单个字符。在 UTF 模式下,字符的长度可以超过一个数据单元。匹配的字符必须在类定义的字符集中,除非类定义的第一个字符是脱字符,在这种情况下,目标字符必须不在类定义的字符集中。如果需要将脱字符作为类的成员,请确保它不是第一个字符,或者使用反斜杠将其转义。

例如,字符类 [aeiou] 匹配任何小写元音,而 [^aeiou] 匹配任何不是小写元音的字符。请注意,脱字符只是通过枚举不在类中的字符来指定类中字符的便捷表示法。以脱字符开头的类不是断言;它仍然会从目标字符串中消耗一个字符,因此,如果当前指针位于字符串的末尾,则它将失败。

在 UTF-8 模式下,值 > 255 (0xffff) 的字符可以作为数据单元的文字字符串包含在类中,或者通过使用 \x{ 转义机制。

当设置不区分大小写的匹配时,类中的任何字母都表示其大写和小写版本。例如,不区分大小写的 [aeiou] 匹配 "A" 和 "a",而不区分大小写的 [^aeiou] 不匹配 "A",但区分大小写的版本会匹配。在 UTF 模式下,PCRE 始终理解值 < 256 的字符的大小写概念,因此始终可以进行不区分大小写的匹配。对于具有较高值的字符,仅当 PCRE 使用 Unicode 属性支持进行编译时才支持大小写概念。如果要在 UTF 模式下对字符 >= 使用不区分大小写的匹配,请确保 PCRE 使用 Unicode 属性支持和 UTF 支持进行编译。

在匹配字符类时,无论使用何种行尾序列,以及使用 PCRE_DOTALLPCRE_MULTILINE 的何种设置,表示换行符的字符都不会以任何特殊方式进行处理。诸如 [^a] 之类的类始终匹配其中一个字符。

连字符 (减号) 可用于指定字符类中的字符范围。例如,[d-m] 匹配 d 和 m 之间的任何字母,包括 d 和 m。如果类中需要连字符,则必须使用反斜杠将其转义,或者出现在无法解释为表示范围的位置,通常是类中的第一个或最后一个字符,或者紧接在范围之后。例如,[b-d-z] 匹配范围 b 到 d 中的字母、连字符或 z。

文字字符 "]" 不能是范围的结束字符。诸如 [W-]46] 之类的模式被解释为两个字符("W" 和 "-")的类,后跟文字字符串 "46]",因此它将匹配 "W46]" 或 "-46]"。但是,如果使用反斜杠转义 "]",则将其解释为范围的结尾,因此 [W-\]46] 被解释为包含范围的类,后跟两个其他字符。也可以使用 "]" 的八进制或十六进制表示来结束范围。

如果在预期范围结束字符的位置出现 POSIX 字符类(见下文)或定义单个字符以外的转义序列,则会生成错误。例如,[z-\xff] 有效,但 [A-\d][A-[:digit:]] 无效。

范围操作基于字符值的排序序列。它们也可以用于以数值方式指定的字符,例如,[\000-\037]。范围可以包括当前模式下有效的任何字符。

如果在设置不区分大小写匹配时使用了包含字母的范围,则它会匹配任意大小写的字母。例如,[W-c] 等价于 [][\\^_`wxyzabc],不区分大小写匹配。在非 UTF 模式下,如果正在使用法语区域设置的字符表,则 [\xc8-\xcb] 会匹配两种大小写的带重音符号的 E 字符。在 UTF 模式下,只有在编译时启用了 Unicode 属性支持时,PCRE 才支持值 > 255 的字符的大小写概念。

字符转义序列 \d\D\h\H\p\P\s\S\v\V\w\W 可以出现在字符类中,并将它们匹配的字符添加到该类中。例如,[\dABCDEF] 匹配任何十六进制数字。在 UTF 模式下,选项 ucp 会影响 \d\s\w 及其大写形式的含义,就像它们出现在字符类外部时一样,如前文 通用字符类型 部分所述。转义序列 \b 在字符类内部具有不同的含义;它匹配退格字符。序列 \B\N\R\X 在字符类内部不是特殊的。像任何其他无法识别的转义序列一样,它们被视为字面字符 "B"、"N"、"R" 和 "X"。

抑扬符可以方便地与大写字符类型一起使用,以指定比匹配的小写类型更受限制的字符集。例如,类 [^\W_] 匹配任何字母或数字,但不包括下划线,而 [\w] 包括下划线。正字符类应理解为“某物或某物或...”,而负字符类应理解为“非某物且非某物且非...”。

只有以下元字符在字符类中被识别

  • 反斜杠
  • 连字符(仅在其可以被解释为指定范围时)
  • 抑扬符(仅在开头)
  • 左方括号(仅在其可以被解释为引入 Posix 类名称或用于特殊兼容性特性时;请参阅接下来的两节)
  • 终止右方括号

但是,转义其他非字母数字字符不会造成任何损害。

Posix 字符类

Perl 支持 Posix 字符类的表示法。它使用由 [::] 括起来的名称,位于外面的方括号内。PCRE 也支持此表示法。例如,以下内容匹配 "0"、"1"、任何字母字符或 "%"

[01[:alpha:]%]

以下是支持的类名称

  • alnum - 字母和数字

  • alpha - 字母

  • blank - 仅限空格或制表符

  • cntrl - 控制字符

  • digit - 十进制数字(与 \d 相同)

  • graph - 可打印字符,不包括空格

  • lower - 小写字母

  • print - 可打印字符,包括空格

  • punct - 可打印字符,不包括字母、数字和空格

  • space - 空白符(与 PCRE 8.34 中的 \s 相同)

  • upper - 大写字母

  • word - “单词”字符(与 \w 相同)

  • xdigit - 十六进制数字

还有另一个字符类 ascii,它错误地匹配 Latin-1 字符,而不是 POSIX 指定的 0-127 范围。如果不改变其他类的行为,就无法修复此问题,因此我们建议使用 [\\0-\x7f] 来匹配该范围。

默认的“空格”字符是 HT (9)、LF (10)、VT (11)、FF (12)、CR (13) 和空格 (32)。如果正在进行特定于区域设置的匹配,则空格字符的列表可能会有所不同;它们可能更少或更多。“空格”过去与 \s 不同,为了与 Perl 兼容,\s 不包括 VT。然而,Perl 在 5.18 版本中进行了更改,PCRE 在 8.34 版本中也进行了更改。“空格”和 \s 现在匹配相同的字符集。

名称“word”是 Perl 扩展,“blank”是来自 Perl 5.8 的 GNU 扩展。另一个 Perl 扩展是取反,它由冒号后的 ^ 字符表示。例如,以下内容匹配 “1”、“2” 或任何非数字

[12[:^digit:]]

PCRE(和 Perl)也识别 Posix 语法 [.ch.][=ch=],其中 “ch” 是 “排序元素”,但这些不受支持,如果遇到它们,则会报错。

默认情况下,值 > 255 的字符不匹配任何 Posix 字符类。但是,如果将选项 PCRE_UCP 传递给 pcre_compile(),则某些类会发生更改,以便使用 Unicode 字符属性。这是通过将某些 Posix 类替换为其他序列来实现的,如下所示

  • [:alnum:] - 变为 \p{Xan}

  • [:alpha:] - 变为 \p{L}

  • [:blank:] - 变为 \h

  • [:digit:] - 变为 \p{Nd}

  • [:lower:] - 变为 \p{Ll}

  • [:space:] - 变为 \p{Xps}

  • [:upper:] - 变为 \p{Lu}

  • [:word:] - 变为 \p{Xwd}

取反版本,例如 [:^alpha:],使用 \P 而不是 \p。在 UCP 模式下,其他三个 POSIX 类会进行特殊处理

  • [:graph:] - 这匹配打印时会在页面上标记字形的字符。在 Unicode 属性方面,它匹配具有 L、M、N、P、S 或 Cf 属性的所有字符,但不包括

    • U+061C - 阿拉伯字母标记

    • U+180E - 蒙古语元音分隔符

    • U+2066 - U+2069 - 各种“隔离符”

  • [:print:] - 这匹配与 [:graph:] 相同的字符,外加不属于控制字符的空格字符,即具有 Zs 属性的字符。

  • [:punct:] - 这匹配所有具有 Unicode P(标点符号)属性的字符,以及代码点小于 128 且具有 S(符号)属性的字符。

其他 POSIX 类保持不变,并且仅匹配代码点小于 128 的字符。

单词边界的兼容性特性

在 4.4BSD Unix 中包含的 POSIX.2 兼容库中,使用丑陋的语法 [[:<:]][[:>:]] 来匹配“单词开头”和“单词结尾”。PCRE 将这些项处理如下

  • [[:<:]] - 转换为 \b(?=\w)

  • [[:>:]] - 转换为 \b(?<=\w)

仅识别这些精确的字符序列。诸如 [a[:<:]b] 之类的序列会因无法识别的 POSIX 类名称而引发错误。此支持与 Perl 不兼容。它旨在帮助从其他环境进行迁移,最好不要在任何新模式中使用。请注意,\b 在单词的开头和结尾处匹配(请参阅上面的“简单断言”),并且在 Perl 样式模式中,前面的字符或后面的字符通常会显示需要哪个,而无需使用上述断言来给出精确的 POSIX 行为。

竖线

竖线字符用于分隔备选模式。例如,以下模式匹配 “gilbert” 或 “sullivan”

gilbert|sullivan

可以出现任意数量的备选方案,并且允许空备选方案(匹配空字符串)。匹配过程从左到右依次尝试每个备选方案,并使用第一个成功的方案。如果备选方案位于子模式中(在 子模式 部分中定义),则“成功”意味着匹配剩余的主模式和子模式中的备选方案。

内部选项设置

Perl 兼容选项 caselessmultilinedotallextended 的设置可以通过模式内部的 Perl 选项字母序列(用 "(?" 和 ")" 括起来)进行更改。选项字母如下

  • i - 用于 caseless

  • m - 用于 multiline

  • s - 用于 dotall

  • x - 用于 extended

例如,(?im) 设置不区分大小写、多行匹配。这些选项也可以通过在字母前面加上连字符来取消设置。允许组合设置和取消设置,例如 (?im-sx),它设置 caselessmultiline,同时取消设置 dotallextended。如果一个字母在连字符之前和之后都出现,则该选项将被取消设置。

PCRE 特定的选项 dupnamesungreedyextra 可以使用字符 J、U 和 X 分别以与 Perl 兼容选项相同的方式进行更改。

当这些选项更改之一发生在顶层(即不在子模式括号内)时,该更改将应用于后面模式的其余部分。

子模式内的选项更改(请参阅 子模式 部分)仅影响后面子模式的那部分。因此,以下内容匹配 abc 和 aBc,不匹配其他字符串(假设未使用 caseless

(a(?i)b)c

通过这种方式,可以使选项在模式的不同部分具有不同的设置。在一个备选方案中所做的任何更改都会延续到同一子模式内的后续分支中。例如

(a(?i)b|c)

匹配 “ab”、“aB”、“c” 和 “C”,尽管在匹配 “C” 时,第一个分支在设置选项之前被放弃。这是因为选项设置的效果发生在编译时。否则会出现一些奇怪的行为。

注意

其他 PCRE 特定的选项可以在调用编译或匹配函数时由应用程序设置。有时,模式可能包含特殊的引导序列,例如 (*CRLF),以覆盖应用程序设置的内容或默认值。详细信息在前面 换行符序列 部分中提供。

可以使用 (UTF8) 和 (UCP) 前导序列来设置 UTF 和 Unicode 属性模式。它们分别等效于设置 unicodeucp 选项。(*UTF) 序列是一个通用版本,可与任何库一起使用。但是,应用程序可以设置 never_utf 选项,该选项会阻止使用 (*UTF) 序列。

子模式

子模式由括号(圆括号)分隔,可以嵌套。将模式的一部分转换为子模式会执行两项操作

  • 1. - 它会本地化一组备选项。例如,以下模式匹配 "cataract"、"caterpillar" 或 "cat"

    cat(aract|erpillar|)

    如果没有括号,它会匹配 "cataract"、"erpillar" 或空字符串。

  • 2. - 它将子模式设置为捕获子模式。也就是说,当完整模式匹配时,与子模式匹配的主题字符串部分会通过 run/3 的返回值传递回调用方。

从左到右(从 1 开始)计算左括号,以获得捕获子模式的编号。例如,如果字符串 "the red king" 与以下模式匹配,则捕获的子字符串为 "red king"、"red" 和 "king",编号分别为 1、2 和 3

the ((red|white) (king|queen))

简单的括号执行两个功能并不总是很有帮助。通常,需要一个分组子模式,而不需要捕获。如果左括号后跟一个问号和一个冒号,则子模式不执行任何捕获,并且在计算任何后续捕获子模式的编号时不会被计算在内。例如,如果字符串 "the white queen" 与以下模式匹配,则捕获的子字符串为 "white queen" 和 "queen",编号分别为 1 和 2

the ((?:red|white) (king|queen))

捕获子模式的最大数量为 65535。

作为一种方便的简写,如果需要在非捕获子模式的开头设置任何选项,则选项字母可以出现在 "?" 和 ":" 之间。因此,以下两个模式匹配同一组字符串

(?i:saturday|sunday)
(?:(?i)saturday|sunday)

由于备用分支是从左到右尝试的,并且选项在到达子模式的末尾之前不会重置,因此一个分支中的选项设置会影响后续分支,因此上面的模式同时匹配 "SUNDAY" 和 "Saturday"。

重复子模式编号

Perl 5.10 引入了一个功能,其中子模式中的每个备选项都为其捕获括号使用相同的编号。这样的子模式以 (?| 开头,本身是一个非捕获子模式。例如,考虑以下模式

(?|(Sat)ur|(Sun))day

由于两个备选项都在 (?| 组内,因此两组捕获括号都编号为一。因此,当模式匹配时,您可以查看捕获的子字符串编号一,无论哪个备选项匹配。当您想捕获多个备选项中的一部分而不是全部时,此构造很有用。在 (?| 组内,括号照常编号,但该编号在每个分支的开头重置。任何跟随子模式的捕获括号的编号都在任何分支中使用的最高编号之后开始。以下示例来自 Perl 文档;下面的数字显示捕获的内容存储在哪个缓冲区中

# before  ---------------branch-reset----------- after
/ ( a )  (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
# 1            2         2  3        2     3     4

对编号子模式的反向引用使用任何子模式为该编号设置的最新值。以下模式匹配 "abcabc" 或 "defdef"

/(?|(abc)|(def))\1/

相比之下,对编号子模式的子例程调用始终引用模式中具有给定编号的第一个子模式。以下模式匹配 "abcabc" 或 "defabc"

/(?|(abc)|(def))(?1)/

如果子模式是否匹配的条件测试引用非唯一编号,则如果该编号的任何子模式匹配,则测试为 true。

使用此“分支重置”功能的另一种方法是使用重复的命名子模式,如下节所述。

命名子模式

通过编号标识捕获括号很简单,但在复杂的正则表达式中很难跟踪编号。此外,如果修改表达式,编号可能会更改。为了帮助解决此难题,PCRE 支持子模式的命名。此功能直到 5.10 版本才添加到 Perl 中。Python 较早拥有此功能,PCRE 在 4.0 版本中引入了该功能,使用了 Python 语法。PCRE 现在同时支持 Perl 和 Python 语法。Perl 允许编号相同的子模式具有不同的名称,但 PCRE 不允许。

在 PCRE 中,可以使用三种方式之一来命名子模式:(?<name>...)(?'name'...),如 Perl 中所示,或者 (?P<name>...),如 Python 中所示。可以按名称和编号从模式的其他部分引用捕获括号,例如反向引用、递归和条件。

名称由最多 32 个字母数字字符和下划线组成,但必须以非数字开头。命名捕获括号仍会分配编号以及名称,就像名称不存在一样。run/3capture 规范可以在正则表达式中存在命名值时使用它们。

默认情况下,名称在模式中必须是唯一的,但可以通过在编译时设置 dupnames 选项来放宽此约束。(对于具有相同编号的子模式,也始终允许重复名称,如上一节所述。)对于只能匹配命名括号的一个实例的模式,重复名称可能很有用。假设您要匹配工作日的名称,可以是 3 个字母的缩写形式,也可以是全名,并且在这两种情况下您都想提取缩写。以下模式(忽略换行符)可以完成此工作

(?<DN>Mon|Fri|Sun)(?:day)?|
(?<DN>Tue)(?:sday)?|
(?<DN>Wed)(?:nesday)?|
(?<DN>Thu)(?:rsday)?|
(?<DN>Sat)(?:urday)?

有五个捕获子字符串,但在匹配后只会设置一个。(解决此问题的另一种方法是使用“分支重置”子模式,如上一节所述。)

对于名称不唯一的捕获命名子模式,如果 capture 语句的 values 部分指定了名称,则从 run/3 返回第一个匹配的匹配项(在主题中从左到右计数)。all_names 捕获值以相同的方式匹配所有名称。

注意

您不能使用不同的名称来区分两个具有相同编号的子模式,因为 PCRE 在匹配时仅使用编号。因此,如果在编译时为具有相同编号的子模式指定不同的名称,则会给出错误。但是,即使未设置 dupnames,您也可以为具有相同编号的子模式指定相同的名称。

重复

重复由量词指定,量词可以跟随以下任何项

  • 字面数据字符
  • 点元字符
  • \C 转义序列
  • \X 转义序列
  • \R 转义序列
  • 匹配单个字符的转义符,如 \d\pL
  • 字符类
  • 反向引用(请参阅下一节)
  • 带括号的子模式(包括断言)
  • 对子模式的子例程调用(递归或其他)

通用重复量词通过在花括号中给出两个数字(用逗号分隔)来指定允许匹配的最小和最大次数。数字必须 < 65536,并且第一个必须小于或等于第二个。例如,以下匹配 "zz"、"zzz" 或 "zzzz"

z{2,4}

单独的右大括号不是特殊字符。如果省略第二个数字,但存在逗号,则没有上限。如果省略第二个数字和逗号,则量词指定所需匹配的确切次数。因此,以下匹配至少三个连续的元音,但可以匹配更多

[aeiou]{3,}

以下匹配恰好八个数字

\d{8}

出现在不允许使用量词的位置的花括号,或者不匹配量词语法的花括号,将被视为字面字符。例如,{,6} 不是量词,而是由四个字符组成的字面字符串。

在 Unicode 模式下,量词应用于字符,而不是单个数据单元。因此,例如,\x{100}{2} 匹配两个字符,每个字符都由 UTF-8 字符串中的 2 字节序列表示。同样,\X{3} 匹配三个 Unicode 扩展字形簇,每个字形簇可以包含多个数据单元(并且它们的长度可能不同)。

允许使用量词 {0},导致表达式的行为就像之前的项和量词不存在一样。这对于从模式的其他位置作为子例程引用的子模式很有用(但也请参阅仅供引用使用的定义子模式部分)。除了具有 {0} 量词的子模式之外的项目将从编译的模式中省略。

为方便起见,三个最常用的量词具有单字符缩写

  • * - 等效于 {0,}

  • + - 等效于 {1,}

  • ? - 等效于 {0,1}

可以通过跟随一个可以不匹配任何字符的子模式(量词没有上限)来构造无限循环,例如

(a?)*

早期版本的 Perl 和 PCRE 过去常常为此类模式在编译时给出错误。但是,由于在某些情况下这很有用,因此现在接受此类模式。但是,如果子模式的任何重复不匹配任何字符,则会强制中断循环。

默认情况下,量词是“贪婪的”,也就是说,它们会匹配尽可能多的内容(直到允许的最大次数),而不会导致其余模式失败。这会导致问题的经典示例是尝试匹配 C 程序中的注释。这些注释出现在 /* 和 */ 之间。在注释中,可能会出现单个 * 和 / 字符。尝试通过应用模式来匹配 C 注释

/\*.*\*/

到字符串

/* first comment */  not comment  /* second comment */

由于 .* 项目的贪婪性,它会匹配整个字符串,导致匹配失败。

然而,如果一个量词后面跟着一个问号,它就会停止贪婪,而是尽可能匹配最少的次数。因此,以下模式可以正确处理 C 语言的注释。

/\*.*?\*/

各种量词的含义不会改变,只是匹配的首选次数会改变。不要将问号的这种用法与其作为量词的用法混淆。因为它有两种用法,所以有时会出现双重问号,例如:

\d??\d

该模式优先匹配一个数字,但如果这是剩余模式匹配的唯一方式,它也可以匹配两个数字。

如果设置了 ungreedy 选项(Perl 中没有此选项),则量词默认不是贪婪的,但是可以通过在其后添加问号使其变为贪婪的。也就是说,它会反转默认行为。

当用大于 1 的最小重复计数或有限的最大重复计数来量化带括号的子模式时,编译后的模式需要更多内存,内存大小与最小或最大值成比例。

如果模式以 .* 或 .{0,} 开头,并且设置了 dotall 选项(等效于 Perl 选项 /s),从而允许点号匹配换行符,则该模式会被隐式锚定,因为无论后续内容是什么,都会尝试与目标字符串中的每个字符位置进行匹配。因此,在第一次匹配之后的任何位置重试整体匹配都没有意义。PCRE 通常将此类模式视为前面带有 \A

在已知目标字符串不包含换行符的情况下,值得设置 dotall 以获得此优化,或者使用 ^ 来显式指示锚定。

但是,在某些情况下,无法使用此优化。当 .* 位于捕获括号内,并且该括号是模式中其他位置的反向引用的主题时,开头的匹配可能会失败,而后面的匹配可能会成功。例如,考虑以下模式:

(.*)abc\1

如果目标字符串是 "xyz123abc123",则匹配点是第四个字符。因此,这样的模式不会被隐式锚定。

不应用隐式锚定的另一种情况是,当开头的 .* 位于原子组内时。同样,开头的匹配可能会失败,而后面的匹配可能会成功。考虑以下模式:

(?>.*?a)b

它在目标字符串 "aab" 中匹配 "ab"。使用回溯控制动词 (PRUNE) 和 (SKIP) 也会禁用此优化。

当重复一个捕获的子模式时,捕获的值是与最后一次迭代匹配的子字符串。例如,在

(tweedle[dume]{3}\s*)+

匹配了 "tweedledum tweedledee" 之后,捕获的子字符串的值是 "tweedledee"。但是,如果有嵌套的捕获子模式,则可以在之前的迭代中设置相应的捕获值。例如,在

/(a|(b))+/

匹配了 "aba" 之后,第二个捕获的子字符串的值是 "b"。

原子分组和占有量词

使用最大化(“贪婪”)和最小化(“非贪婪”或“惰性”)重复时,如果后面的内容匹配失败,通常会重新评估重复的项目,以查看不同的重复次数是否允许剩余的模式匹配。有时,为了更改匹配的性质,或者在模式的作者知道继续下去毫无意义时,使其比其他情况下更早失败,可以阻止这种情况发生。

例如,考虑将模式 \d+foo 应用于以下目标行:

123456bar

在匹配了所有六个数字,然后匹配 "foo" 失败之后,匹配器的正常操作是尝试仅使用五个数字匹配 \d+ 项,然后使用四个数字,依此类推,最终失败。“原子分组”(该术语取自 Jeffrey Friedl 的书)提供了一种指定一旦子模式匹配,就不应以这种方式重新评估它的方法。

如果对前面的示例使用原子分组,则匹配器在第一次匹配 "foo" 失败时立即放弃。其表示法是一种特殊的括号,以 (?> 开头,如下例所示:

(?>\d+)foo

这种括号会“锁定”它所包含的模式部分,一旦它匹配,模式中更靠后的失败就会阻止回溯到其中。然而,回溯到它之前的项目像往常一样工作。

另一种描述是,这种类型的子模式匹配与当前目标字符串中锚定的相同独立模式将匹配的字符字符串。

原子分组子模式不是捕获子模式。可以将上述简单示例视为必须吞噬它所能吞噬的一切的最大化重复。因此,虽然 \d+\d+? 都准备调整它们匹配的数字数量以使剩余的模式匹配,但 (?>\d+) 只能匹配整个数字序列。

通常,原子组可以包含任何复杂的子模式,并且可以嵌套。但是,当原子组的子模式只是一个重复的项目时,如上面的示例所示,可以使用更简单的表示法,称为“占有量词”。它由量词后面的额外 + 字符组成。使用此表示法,前面的示例可以重写为:

\d++foo

请注意,占有量词可以与整个组一起使用,例如:

(abc|xyz){2,3}+

占有量词始终是贪婪的;忽略 ungreedy 选项的设置。它们是原子组的更简单形式的便捷表示法。但是,占有量词和等效的原子组的含义没有区别,但性能可能存在差异;占有量词可能稍微快一些。

占有量词语法是 Perl 5.8 语法的扩展。Jeffrey Friedl 在他的书的第一版中提出了这个想法(和名称)。Mike McCloskey 很喜欢它,所以在他构建 Sun Java 包时实现了它,PCRE 从那里复制了它。它最终在 5.10 版本中进入了 Perl。

PCRE 有一个优化,可以自动“占有化”某些简单的模式构造。例如,序列 A+B 被视为 A++B,因为当 B 必须跟随 A 时,回溯到 A 的序列中是没有意义的。

当一个模式包含一个子模式内的无限重复,而该子模式本身可以无限重复时,使用原子组是避免某些匹配失败花费很长时间的唯一方法。该模式:

(\D+|<\d+>)*[!?]

匹配无限数量的子字符串,这些子字符串由非数字组成,或者由括在 <> 中的数字组成,后跟 !?。当它匹配时,它会快速运行。但是,如果将其应用于

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

则需要很长时间才能报告失败。这是因为字符串可以在内部 \D+ 重复和外部 * 重复之间以多种方式划分,并且必须尝试所有方式。(该示例使用 [!?] 而不是末尾的单个字符,因为 PCRE 和 Perl 都有一个优化,允许在使用单个字符时快速失败。它们会记住匹配所需的最后一个单字符,如果该字符不存在于字符串中,则会提前失败。)如果更改模式以使其使用原子组,如下所示,则无法中断非数字序列,并且会快速发生失败:

((?>\D+)|<\d+>)*[!?]

反向引用

在字符类之外,后跟一个数字 > 0(并且可能还有更多数字)的反斜杠是对模式中较早的(即在其左侧)捕获子模式的反向引用,前提是之前存在那么多捕获的左括号。

但是,如果反斜杠后面的十进制数 < 10,则始终将其视为反向引用,并且仅当整个模式中没有那么多捕获的左括号时才会导致错误。也就是说,被引用的括号不需要在数字 < 10 的引用的左侧。“前向反向引用”在这种类型中,当涉及重复并且右侧的子模式参与了之前的迭代时,才有意义。

不可能使用此语法对数字为 10 或更大的子模式进行数值“前向反向引用”,因为诸如 \50 之类的序列被解释为以八进制定义的字符。有关处理反斜杠后数字的更多详细信息,请参阅前面的 非打印字符部分。使用命名括号时,不存在此类问题。可以使用命名括号对任何子模式进行反向引用(见下文)。

避免反斜杠后使用数字所固有的歧义的另一种方法是使用 \g 转义序列。此转义必须后跟一个无符号数或一个负数,可以选择用大括号括起来。以下示例是相同的:

(ring), \1
(ring), \g1
(ring), \g{1}

无符号数指定一个绝对引用,而没有旧语法中存在的歧义。当文字数字跟随引用时,它也很有用。负数是相对引用。考虑以下示例:

(abc(def)ghi)\g{-1}

序列 \g{-1} 是对 \g 之前最近启动的捕获子模式的引用,也就是说,在本例中它等效于 \2。类似地,\g{-2} 将等效于 \1。在长模式中,以及在通过连接包含自身引用的片段创建的模式中,使用相对引用可能很有帮助。

反向引用匹配当前目标字符串中与捕获子模式匹配的内容,而不是匹配子模式本身的内容(子模式作为子程序部分描述了一种执行此操作的方法)。因此,以下模式匹配 "sense and sensibility" 和 "response and responsibility",但不匹配 "sense and responsibility":

(sens|respons)e and \1ibility

如果在反向引用时强制执行区分大小写的匹配,则字母的大小写是相关的。例如,以下模式匹配 "rah rah" 和 "RAH RAH",但不匹配 "RAH rah",尽管原始捕获子模式不区分大小写匹配:

((?i)rah)\s+\1

有很多不同的方法来编写对命名子模式的反向引用。.NET 语法 \k{name} 和 Perl 语法 \k<name>\k'name' 均受支持,Python 语法 (?P=name) 也受支持。还支持 Perl 5.10 中的统一反向引用语法,其中 \g 可用于数字引用和命名引用。前面的示例可以用以下方式重写:

(?<p1>(?i)rah)\s+\k<p1>
(?'p1'(?i)rah)\s+\k{p1}
(?P<p1>(?i)rah)\s+(?P=p1)
(?<p1>(?i)rah)\s+\g{p1}

通过名称引用的子模式可以出现在模式中引用的之前或之后。

可以存在对同一子模式的多个反向引用。如果子模式在特定匹配中未使用,则对它的任何反向引用始终会失败。例如,如果以下模式开始匹配 "a" 而不是 "bc",则该模式始终会失败:

(a|(bc))\2

由于模式中可能存在多个捕获括号,所以反斜杠后的所有数字都被视为潜在的反向引用数字的一部分。如果模式继续使用数字字符,则必须使用某种分隔符来终止反向引用。如果设置了 extended 选项,则可以使用空格作为分隔符。否则,可以使用空注释(请参阅 注释 部分)。

递归反向引用

在它所引用的括号内部出现反向引用时,当第一次使用子模式时会失败,因此,例如 (a\1) 永远不会匹配。但是,这种引用在重复的子模式中可能很有用。例如,以下模式匹配任意数量的 "a" 以及 "aba"、"ababbaa" 等

(a|b\1)+

在子模式的每次迭代中,反向引用匹配与上一次迭代对应的字符串。为了使此工作正常,模式必须确保第一次迭代不需要匹配反向引用。这可以使用交替来实现,如上面的示例中所示,或者使用最小值为零的量词。

这种类型的反向引用会导致它们引用的组被视为原子组。一旦整个组被匹配,后续的匹配失败不会导致回溯到组的中间。

断言

断言是对当前匹配点之后或之前的字符进行的测试,它不消耗任何字符。简单的断言编码为 \b\B\A\G\Z\z^$,在前面的章节中已经描述过。

更复杂的断言被编码为子模式。有两种类型:一种是向前查看主题字符串中当前位置的,另一种是向后查看的。断言子模式以正常方式匹配,但它不会导致当前匹配位置发生更改。

断言子模式不是捕获子模式。如果这样的断言包含捕获子模式,则这些子模式会被计入整个模式中捕获子模式的编号。但是,子字符串捕获仅针对肯定断言执行。(Perl 有时,但不总是,在否定断言中执行捕获。)

警告

如果包含一个或多个捕获子模式的肯定断言成功,但是模式中稍后匹配失败导致回溯到此断言,则只有在没有设置更高编号的捕获时,断言内的捕获才会被重置。不幸的是,这是当前实现的一个根本限制,并且由于 PCRE1 现在处于仅维护状态,因此不太可能发生更改。

为了与 Perl 兼容,断言子模式可以重复。但是,多次断言同一件事没有意义,捕获括号的副作用有时可能很有用。实际上,只有三种情况

  • 如果量词为 {0},则在匹配期间永远不会遵守断言。但是,它可以包含内部捕获括号组,这些组通过子例程机制从其他地方调用。
  • 如果量词为 {0,n},其中 n > 0,则将其视为 {0,1}。在运行时,剩余的模式匹配会尝试使用和不使用断言,顺序取决于量词的贪婪程度。
  • 如果最小重复次数 > 0,则忽略量词。在匹配期间遇到时,断言只会被遵守一次。

前瞻断言

前瞻断言以 (?= 表示肯定断言,以 (?! 表示否定断言。例如,以下模式匹配一个后跟分号的单词,但不将分号包含在匹配项中

\w+(?=;)

以下模式匹配任何不后跟 "bar" 的 "foo" 的出现

foo(?!bar)

请注意,看似相似的模式

(?!foo)bar

找不到前面不是 "foo" 的 "bar" 的出现。它会找到任何 "bar" 的出现,因为当接下来的三个字符是 "bar" 时,断言 (?!foo) 始终为真。需要后瞻断言才能实现另一个效果。

如果您想强制模式中某个点的匹配失败,最方便的方法是使用 (?!),因为空字符串始终匹配。因此,要求不存在空字符串的断言必须始终失败。回溯控制动词 (FAIL) 或 (F) 是 (?! ) 的同义词。

后瞻断言

后瞻断言以 (?<= 表示肯定断言,以 (?<! 表示否定断言。例如,以下模式查找前面不是 "foo" 的 "bar" 的出现

(?<!foo)bar

后瞻断言的内容受到限制,使得它匹配的所有字符串都必须具有固定长度。但是,如果存在许多顶级备选项,则它们不必都具有相同的固定长度。因此,允许以下情况

(?<=bullock|donkey)

以下情况会在编译时导致错误

(?<!dogs?|cats?)

匹配不同长度字符串的分支只允许在后瞻断言的顶层。这是与 Perl 相比的扩展,Perl 要求所有分支匹配相同长度的字符串。不允许以下形式的断言,因为它的单个顶级分支可以匹配两种不同的长度

(?<=ab(c|de))

但是,如果重写为使用两个顶级分支,则 PCRE 可以接受

(?<=abc|abde)

有时,可以使用转义序列 \K (见上文)而不是后瞻断言来绕过固定长度限制。

后瞻断言的实现方式是,对于每个备选项,将当前位置临时后退固定长度,然后尝试匹配。如果当前位置之前没有足够的字符,则断言失败。

在 UTF 模式下,PCRE 不允许 \C 转义符(即使在 UTF 模式下也匹配单个数据单元)出现在后瞻断言中,因为它无法计算后瞻的长度。\X\R 转义符(可以匹配不同数量的数据单元)也不允许使用。

“子例程”调用(见下文),例如 (?2) 或 (?&X),允许在后瞻中使用,只要子模式匹配固定长度的字符串即可。但是,不支持递归。

占有量词可以与后瞻断言一起使用,以指定在主题字符串末尾高效匹配固定长度的字符串。考虑以下简单模式,当应用于不匹配的长字符串时

abcd$

由于匹配是从左到右进行的,因此 PCRE 会在主题中查找每个 "a",然后查看后面是否匹配剩余的模式。如果模式指定为

^.*abcd$

最初 .* 匹配整个字符串。但是,当它失败时(因为后面没有 "a"),它会回溯以匹配除最后一个字符之外的所有字符,然后是除最后两个字符之外的所有字符,依此类推。再次,对 "a" 的搜索覆盖整个字符串,从右到左,因此我们并没有变得更好。但是,如果模式写为

^.*+(?<=abcd)

则 .*+ 项不能回溯;它只能匹配整个字符串。随后的后瞻断言对最后四个字符进行单次测试。如果它失败,则匹配立即失败。对于长字符串,这种方法对处理时间有显著的影响。

使用多个断言

可以连续出现多个(任何类型的)断言。例如,以下模式匹配前面带有三个数字而不是 "999" 的 "foo"

(?<=\d{3})(?<!999)foo

请注意,每个断言都在主题字符串中的同一点独立应用。首先检查前三个字符是否都是数字,然后再检查相同的三个字符是否不是 "999"。此模式匹配前面有六个字符的 "foo",其中前三个是数字,最后三个不是 "999"。例如,它不匹配 "123abcfoo"。一个执行此操作的模式如下

(?<=\d{3}...)(?<!999)foo

这一次,第一个断言查看前面的六个字符,检查前三个是否是数字,然后第二个断言检查前面的三个字符是否不是 "999"。

断言可以以任何组合嵌套。例如,以下模式匹配前面带有 "bar" 的 "baz" 的出现,而 "bar" 前面又不是 "foo"

(?<=(?<!foo)bar)baz

以下模式匹配前面有三个数字和任意三个字符的 "foo",这三个字符不是 "999"

(?<=\d{3}(?!999)...)foo

条件子模式

可以使匹配过程有条件地遵守子模式,或者根据断言的结果,或者是否已经匹配了特定的捕获子模式,在两个备选子模式之间进行选择。以下是条件子模式的两种可能形式

(?(condition)yes-pattern)
(?(condition)yes-pattern|no-pattern)

如果条件满足,则使用 yes-pattern,否则使用 no-pattern(如果存在)。如果子模式中存在两个以上的备选项,则会发生编译时错误。两个备选项中的每一个都可以包含任何形式的嵌套子模式,包括条件子模式;对两个备选项的限制仅适用于条件级别。以下模式片段是一个示例,其中备选项很复杂

(?(1) (A|B|C) | (D | (?(2)E|F) | E) )

条件有四种:对子模式的引用、对递归的引用、称为 DEFINE 的伪条件和断言。

按数字检查使用的子模式

如果括号之间的文本包含一系列数字,则如果先前已匹配该数字的捕获子模式,则条件为真。如果存在多个具有相同编号的捕获子模式(请参阅前面的 重复子模式编号 部分),则如果其中任何一个已匹配,则条件为真。另一种表示法是在数字前面加上加号或减号。在这种情况下,子模式编号是相对的而不是绝对的。最近打开的括号可以通过 (?(-1) 引用,下一个最近的括号通过 (?(-2) 引用,依此类推。在循环内部,引用后续组也可能很有意义。要打开的下一个括号可以引用为 (?(+1),依此类推。(在这些形式中的任何一种中,值零都不使用;它会引发编译时错误。)

考虑以下模式,该模式包含不重要的空格以使其更具可读性(假设选项 extended)并将其分为三个部分以便于讨论

( \( )?    [^()]+    (?(1) \) )

第一部分匹配一个可选的开括号,如果存在该字符,则将其设置为第一个捕获的子字符串。第二部分匹配一个或多个不是括号的字符。第三部分是一个条件子模式,用于测试是否匹配了第一组括号。如果匹配了,也就是说,如果主题以开括号开头,则条件为真,因此将执行 yes-pattern,并且需要一个闭括号。否则,由于不存在 no-pattern,因此子模式不匹配任何内容。也就是说,此模式匹配一系列非括号,可选地用括号括起来。

如果此模式嵌入到更大的模式中,则可以使用相对引用

...other stuff... ( \( )?    [^()]+    (?(-1) \) ) ...

这使得该片段独立于较大模式中的括号。

按名称检查使用的子模式

Perl 使用语法 (?(<name>)...) 或 (?('name')...) 按名称测试使用的子模式。为了与早期版本的 PCRE 兼容,PCRE 在 Perl 之前就具有此功能,语法 (?(name)...) 也被识别。

重写之前的示例以使用命名的子模式,如下所示

(?<OPEN> \( )?    [^()]+    (?(<OPEN>) \) )

如果在此类条件中使用的名称是重复的,则测试将应用于所有同名的子模式,并且如果其中任何一个子模式已匹配,则测试为真。

检查模式递归

如果条件是字符串 (R),并且没有名称为 R 的子模式,则如果已对整个模式或任何子模式进行了递归调用,则条件为真。如果字母 R 后面跟着数字或以与号开头的名称,例如

(?(R3)...) or (?(R&name)...)

如果最近一次递归是进入指定编号或名称的子模式,则此条件为真。此条件不检查整个递归堆栈。如果此类型条件中使用的名称是重复的,则测试将应用于所有同名子模式,只要其中任何一个是最新的递归,结果就为真。

在“顶层”,所有这些递归测试条件均为假。递归模式的语法如下所述。

定义仅供引用的子模式

如果条件是字符串 (DEFINE),并且没有名为 DEFINE 的子模式,则条件始终为假。在这种情况下,子模式中只能有一个备选项。如果控制流到达模式中的此点,它总是会被跳过。DEFINE 的想法是它可以用于定义可以从其他地方引用的“子例程”。(子例程的用法如下所述。)例如,匹配 IPv4 地址(如“192.168.23.245”)的模式可以这样编写(忽略空格和换行符)

(?(DEFINE) (?<byte> 2[0-4]\d | 25[0-5] | 1\d\d | [1-9]?\d) ) \b (?&byte) (\.(?&byte)){3} \b

模式的第一部分是 DEFINE 组,其中定义了另一个名为 “byte” 的组。这匹配 IPv4 地址的单个组成部分(一个小于 256 的数字)。当进行匹配时,模式的这部分会被跳过,因为 DEFINE 的作用类似于假条件。剩余的模式使用对命名组的引用来匹配 IPv4 地址的四个点分隔的组成部分,并坚持在每一端都有一个单词边界。

断言条件

如果条件不符合上述任何格式,则它必须是断言。这可以是正向或负向先行断言或后行断言。考虑以下模式,其中包含不重要的空格,并且第二行上有两个备选项

(?(?=[^a-z]*[a-z])
\d{2}-[a-z]{3}-\d{2}  |  \d{2}-\d{2}-\d{2} )

该条件是一个正向先行断言,它匹配一个可选的非字母序列,后跟一个字母。也就是说,它测试主题中是否存在至少一个字母。如果找到一个字母,则使用第一个备选项匹配主题,否则使用第二个备选项匹配主题。此模式匹配两种形式的字符串:dd-aaa-dd 或 dd-dd-dd,其中 aaa 是字母,dd 是数字。

注释

有两种方法可以在 PCRE 处理的模式中包含注释。在这两种情况下,注释的开头都不能在字符类中,也不能在任何其他相关字符序列的中间,例如 (?: 或子模式名称或数字。组成注释的字符在模式匹配中不起任何作用。

序列 (?# 标记注释的开始,该注释一直持续到下一个右括号。不允许嵌套括号。如果设置了选项 PCRE_EXTENDED,则未转义的 # 字符也会引入注释,在这种情况下,注释会一直持续到模式中下一个换行符或字符序列之后。哪些字符被解释为换行符由传递给编译函数的选项或模式开头的特殊序列控制,如前面 换行符约定 部分所述。

请注意,此类型注释的结尾是模式中的文字换行符序列;碰巧表示换行符的转义序列不算数。例如,当 extended 被设置并且默认换行符约定生效时,请考虑以下模式

abc #comment \n still comment

在遇到字符 # 时,pcre_compile() 会跳过,在模式中查找换行符。序列 \n 在此阶段仍然是字面的,因此它不会终止注释。只有代码值为 0x0a(默认换行符)的字符才会这样做。

递归模式

考虑匹配括号中的字符串的问题,允许无限嵌套的括号。如果不使用递归,所能做的最好的就是使用一个匹配到一定固定嵌套深度的模式。无法处理任意嵌套深度。

一段时间以来,Perl 提供了一种允许正则表达式递归(以及其他功能)的工具。它通过在运行时在表达式中插入 Perl 代码来实现这一点,并且该代码可以引用表达式本身。可以使用代码插值创建一个 Perl 模式来解决括号问题,如下所示

$re = qr{\( (?: (?>[^()]+) | (?p{$re}) )* \)}x;

项 (?p{...}) 在运行时插入 Perl 代码,在本例中,它递归地引用它出现的模式。

显然,PCRE 不能支持 Perl 代码的插入。相反,它支持用于整个模式递归和单个子模式递归的特殊语法。在 PCRE 和 Python 中引入这种递归之后,Perl 在 5.10 版本中也引入了这种递归。

由 (? 后跟一个数字 > 0 和一个右括号组成的特殊项是给定编号子模式的递归子例程调用,如果它发生在该子模式内部。(如果不是,则它是非递归子例程调用,将在下一节中描述。)特殊项 (?R) 或 (?0) 是对整个正则表达式的递归调用。

此 PCRE 模式解决了嵌套括号问题(假设设置了选项 extended,以便忽略空格)

\( ( [^()]++ | (?R) )* \)

首先,它匹配一个左括号。然后,它匹配任意数量的子字符串,这些子字符串可以是任意数量的非括号序列,也可以是模式本身的递归匹配(即,正确加上括号的子字符串)。最后是一个右括号。请注意使用占有量词来避免回溯到非括号序列中。

如果这是较大模式的一部分,则您不希望递归整个模式,因此您可以改为使用

( \( ( [^()]++ | (?1) )* \) )

这里的模式在括号内,以便递归引用它们而不是整个模式。

在更大的模式中,跟踪括号编号可能很棘手。通过使用相对引用可以更容易地完成此操作。您可以在上面的模式中使用 (?-2) 而不是 (?1) 来引用递归之前最近打开的第二个括号。也就是说,负数从遇到它的点开始向左计算捕获括号。

也可以通过编写诸如 (?+2) 之类的引用来引用稍后打开的括号。但是,这些不能是递归的,因为引用不在被引用的括号内。它们始终是非递归子例程调用,如下一节所述。

另一种方法是使用命名括号。Perl 的语法是 (?&name)。也支持较早的 PCRE 语法 (?P>name)。我们可以将上面的例子改写如下

(?<pn> \( ( [^()]++ | (?&pn) )* \) )

如果存在多个具有相同名称的子模式,则使用最早的子模式。

我们研究的这个特定示例模式包含嵌套的无限重复,因此当将模式应用于不匹配的字符串时,使用占有量词匹配非括号字符串非常重要。例如,当此模式应用于

(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()

它很快给出“不匹配”。但是,如果不使用占有量词,则匹配会运行很长时间,因为 + 和 * 重复可以划分主题的方式有很多种,并且必须在报告失败之前对其进行测试。

在匹配结束时,捕获括号的值是来自最外层的值。如果上面的模式与

(ab(cd)ef)

匹配,则内部捕获括号(编号为 2)的值为 "ef",这是在顶层取得的最后一个值。如果一个捕获子模式在顶层未匹配,则其最终捕获的值将未设置,即使它在匹配过程中在更深的层次上(暂时)设置了也是如此。

不要将项 (?R) 与条件 (R) 混淆,后者测试递归。考虑以下模式,它匹配尖括号中的文本,允许任意嵌套。嵌套括号(即递归时)中只允许数字,而外部级别允许任何字符。

< (?: (?(R) \d++  | [^<>]*+) | (?R)) * >

这里的 (?(R) 是条件子模式的开始,对于递归和非递归情况,有两个不同的备选项。项 (?R) 是实际的递归调用。

PCRE 和 Perl 之间递归处理的差异

PCRE 中的递归处理在两个重要方面与 Perl 不同。在 PCRE 中(与 Python 类似,但与 Perl 不同),递归子模式调用始终被视为原子组。也就是说,一旦它匹配了主题字符串的某些部分,即使它包含未尝试的备选项并且随后出现匹配失败,它也永远不会重新进入。以下模式可以说明这一点,该模式旨在匹配包含奇数个字符的回文串(例如,“a”、“aba”、“abcba”、“abcdcba”)

^(.|(.)(?1)\2)$

其想法是,它要么匹配单个字符,要么匹配一个子回文周围的两个相同字符。在 Perl 中,此模式有效;在 PCRE 中,如果模式长度超过三个字符,则此模式无效。考虑主题字符串“abcba”。

在顶层,匹配第一个字符,但由于它不在字符串的末尾,因此第一个备选项失败,采用第二个备选项,并开始递归。对子模式 1 的递归调用成功匹配了下一个字符 ("b")。(请注意,行首和行尾测试不是递归的一部分。)

回到顶层,将下一个字符 ("c") 与子模式 2 匹配的内容(即 "a")进行比较。这失败了。由于递归被视为原子组,因此现在没有回溯点,因此整个匹配失败。(Perl 现在可以重新进入递归并尝试第二个备选项。)但是,如果以其他顺序编写备选项的模式,则情况会有所不同

^((.)(?1)\2|.)$

这次,首先尝试递归备选项,并继续递归直到用完字符,此时递归失败。但是这次我们在更高层尝试另一个备选项。这就是显着的差异:在之前的例子中,剩余的备选项位于更深的递归级别,PCRE 无法使用它。

要更改模式,使其匹配所有回文串,而不仅仅是那些具有奇数个字符的回文串,很可能会将模式更改为此

^((.)(?1)\2|.?)$

同样,这在 Perl 中有效,但在 PCRE 中无效,原因相同。当更深的递归匹配单个字符时,无法再次进入以匹配空字符串。解决方案是将这两种情况分开,并在更高层将奇数和偶数情况写成备选项

^(?:((.)(?1)\2|)|((.)(?3)\4|.))

如果要匹配典型的回文短语,则该模式必须忽略所有非单词字符,这可以通过以下方式完成

^\W*+(?:((.)\W*+(?1)\W*+\2|)|((.)\W*+(?3)\W*+\4|\W*+.\W*+))\W*+$

如果使用 caseless 选项运行,此模式将匹配诸如“A man, a plan, a canal: Panama!”之类的短语,并且在 PCRE 和 Perl 中都运行良好。请注意使用占有量词 *+ 来避免回溯到非单词字符序列中。如果没有这个,PCRE 匹配典型短语的时间会更长(10 倍或更多),而 Perl 则需要很长时间,以至于您会认为它进入了一个循环。

注意

只有当主题字符串不以比整个字符串短的回文开头时,上面的回文匹配模式才起作用。例如,虽然 “abcba” 可以正确匹配,但如果主题是 “ababa”,PCRE 会在开头找到回文 “aba”,然后在顶层失败,因为字符串的结尾不跟随。同样,它无法跳回到递归中尝试其他替代方案,因此整个匹配都会失败。

PCRE 和 Perl 在递归处理方面的第二个区别在于对捕获值的处理。在 Perl 中,当子模式被递归调用或作为子模式调用时(请参阅下一节),它无法访问在递归之外捕获的任何值。在 PCRE 中,可以引用这些值。考虑以下模式

^(.)(\1|a(?2))

在 PCRE 中,它匹配 “bab”。第一个捕获括号匹配 “b”,然后在第二个组中,当反向引用 \1 无法匹配 “b” 时,第二个替代方案匹配 “a”,然后进行递归。在递归中,\1 现在匹配 “b”,因此整个匹配成功。在 Perl 中,该模式无法匹配,因为在递归调用内部,\1 无法访问外部设置的值。

作为子例程的子模式

如果在其引用的括号之外使用递归子模式调用(按数字或名称)的语法,则它的行为类似于编程语言中的子例程。被调用的子模式可以在引用之前或之后定义。编号的引用可以是绝对的或相对的,如下例所示

(...(absolute)...)...(?2)...
(...(relative)...)...(?-1)...
(...(?+1)...(relative)...

前面的一个例子指出,以下模式匹配 “sense and sensibility” 和 “response and responsibility”,但不匹配 “sense and responsibility”

(sens|respons)e and \1ibility

如果改为使用以下模式,它将匹配 “sense and responsibility” 和其他两个字符串

(sens|respons)e and (?1)ibility

在前面关于 DEFINE 的讨论中提供了另一个示例。

所有子例程调用(无论是递归的还是非递归的)都始终被视为原子组。也就是说,一旦子例程匹配了主题字符串的某些部分,它就永远不会被重新进入,即使它包含未尝试的替代方案并且随后出现匹配失败。在子例程调用期间设置的任何捕获括号都会在之后恢复为其先前的值。

诸如大小写无关之类的处理选项在定义子模式时是固定的,因此如果将其用作子例程,则对于不同的调用不能更改此类选项。例如,以下模式匹配 “abcabc” 但不匹配 “abcABC”,因为处理选项的更改不会影响调用的子模式

(abc)(?i:(?-1))

Oniguruma 子例程语法

为了与 Oniguruma 兼容,非 Perl 语法 \g 后跟用尖括号或单引号括起来的名称或数字,是引用子模式作为子例程的替代语法,可能递归。下面是上面使用的两个示例,使用此语法重写

(?<pn> \( ( (?>[^()]+) | \g<pn> )* \) )
(sens|respons)e and \g'1'ibility

PCRE 支持 Oniguruma 的扩展:如果数字前面有加号或减号,则将其视为相对引用,例如

(abc)(?i:\g<-1>)

请注意,\g{...} (Perl 语法) 和 \g<...> (Oniguruma 语法) 不是同义词。前者是反向引用;后者是子例程调用。

回溯控制

Perl 5.10 引入了一些“特殊回溯控制动词”,这些动词在 Perl 文档中仍然被描述为“实验性的,并且可能会在 Perl 的未来版本中更改或删除”。它继续说:“在生产代码中使用它们应该注意,以避免在升级期间出现问题。” 同样的评论也适用于本节中描述的 PCRE 功能。

新的动词利用了以前无效的语法:一个左括号后跟一个星号。它们通常的形式是 (VERB) 或 (VERB:NAME)。有些可以采用这两种形式,可能会根据是否存在名称而表现不同。名称是不包含右括号的任何字符序列。在 8 位库中,最大名称长度为 255,在 16 位和 32 位库中为 65535。如果名称为空,即,如果右括号紧跟在冒号之后,则效果就好像冒号不存在一样。这些动词中的任意数量都可以在模式中出现。

下面描述了这些动词在重复组、断言以及作为子例程调用(无论是否递归)的子模式中的行为。

影响回溯动词的优化

PCRE 包含一些优化,这些优化用于通过在每次匹配尝试开始时运行一些检查来加快匹配速度。例如,它可以知道匹配主题的最小长度,或者必须存在特定的字符。当这些优化之一绕过匹配的运行时,任何包含的回溯动词都不会被处理。您可以通过在调用 compile/2run/3 时设置选项 no_start_optimize,或者通过使用 (*NO_START_OPT) 开始模式来禁止匹配开始优化。

对 Perl 的实验表明,它也有类似的优化,有时会导致异常结果。

立即执行的动词

以下动词在遇到时立即执行。它们后面不能跟名称。

(*ACCEPT)

此动词使匹配成功结束,跳过模式的其余部分。但是,当它位于作为子例程调用的子模式内部时,只有该子模式成功结束。然后,匹配在外层继续。如果在肯定断言中触发 (*ACCEPT),则断言成功;在否定断言中,断言失败。

如果 (*ACCEPT) 在捕获括号内,则会捕获到目前为止的数据。例如,以下内容匹配“AB”、“AAD”或“ACD”。当它匹配“AB”时,“B”会被外层括号捕获。

A((?:A|B(*ACCEPT)|C)D)

以下动词会导致匹配失败,强制发生回溯。它等效于 (?!),但更容易阅读。

(*FAIL) or (*F)

Perl 文档指出,它可能仅在与 (?{}) 或 (??{}) 结合使用时才有用。这些是 PCRE 中不存在的 Perl 功能。

与字符串“aaaa”的匹配总是失败,但在每次回溯发生之前都会执行调用(在本示例中为 10 次)。

记录采取了哪条路径

此动词的主要目的是跟踪匹配是如何达到的,尽管它在推进匹配起始点方面也有次要用途(请参阅下面的 (*SKIP))。

注意

在 Erlang 中,没有接口使用 run/2,3 检索标记,因此只有次要用途与 Erlang 程序员相关。

因此,本节的其余部分是故意不为 Erlang 程序员阅读而改编的,但是这些示例可以帮助理解 NAMES,因为它们可以被 (*SKIP) 使用。

(*MARK:NAME) or (*:NAME)

此动词始终需要一个名称。模式中可以有任意数量的 (*MARK) 实例,并且它们的名称不必是唯一的。

当匹配成功时,匹配路径上最后遇到的 (MARK:NAME)、(PRUNE:NAME) 或 (THEN:NAME) 的名称将作为 pcreapi 文档中“pcre_exec() 的额外数据”部分所述的方式传递回调用者。在以下 pcretest 输出示例中,/K 修饰符请求检索和输出 (MARK) 数据

re> /X(*MARK:A)Y|X(*MARK:B)Z/K
data> XY
 0: XY
MK: A
XZ
 0: XZ
MK: B

在此输出中,(*MARK) 名称标记为 "MK:",在此示例中,它指示匹配了两个替代方案中的哪一个。这是一种比将每个替代方案放在其自己的捕获括号中更有效的方法来获取此信息。

如果在肯定断言中遇到带名称的动词,并且该断言为真,则会记录该名称,如果它是最后遇到的名称,则会传递回该名称。这不会发生在否定断言或失败的肯定断言中。

在部分匹配或匹配失败后,将返回整个匹配过程中最后遇到的名称,例如

re> /X(*MARK:A)Y|X(*MARK:B)Z/K
data> XP
No match, mark = B

请注意,在此未锚定的示例中,标记保留自主题中字母 “X” 处开始的匹配尝试。从 “P” 开始的后续匹配尝试,然后使用空字符串,不会到达 (*MARK) 项,但不会重置它。

回溯后执行的动词

以下动词在遇到时什么也不做。匹配将继续进行,但如果没有后续匹配,导致回溯到该动词,则会强制失败。也就是说,回溯不能传递到该动词的左侧。但是,当这些动词之一出现在原子组或为真的断言内部时,其效果仅限于该组,因为一旦组匹配,就永远不会回溯到其中。在这种情况下,回溯可以“跳回”到整个原子组或断言的左侧。(还要记住,如上所述,这种本地化也适用于子例程调用。)

这些动词在回溯到达它们时所发生的失败类型上有所不同。下面描述的行为是当该动词不在子例程或断言中时发生的行为。随后的部分涵盖了这些特殊情况。

如果后续的匹配失败导致回溯到达以下动词(其后面不能跟名称),则该动词将导致整个匹配完全失败。即使模式未锚定,也不会再尝试通过推进起始点来查找匹配项。

(*COMMIT)

如果 (*COMMIT) 是遇到的唯一回溯动词,则一旦它被传递,run/2,3 将致力于在当前起始点查找匹配项,或者根本不查找,例如

a+(*COMMIT)b

这匹配 "xxaab",但不匹配 "aacaab"。 可以将其视为一种动态锚点,或者说“我已经开始了,所以我必须完成”。 当 (COMMIT) 强制匹配失败时,会返回路径中最近通过的 (MARK) 的名称。

如果模式中存在多个回溯动词,则可以首先触发后面跟随 (*COMMIT) 的另一个回溯动词,因此仅仅在匹配期间通过 (*COMMIT) 并不能始终保证匹配必须从这个起始点开始。

请注意,模式开头的 (*COMMIT) 与锚点不同,除非关闭 PCRE 的匹配开始优化,如下例所示

1> re:run("xyzabc","(*COMMIT)abc",[{capture,all,list}]).
{match,["abc"]}
2> re:run("xyzabc","(*COMMIT)abc",[{capture,all,list},no_start_optimize]).
nomatch

对于此模式,PCRE 知道任何匹配都必须以“a”开头,因此优化会跳过主题,在将模式应用于第一组数据之前先跳到“a”。 然后匹配尝试成功。 在第二次调用中,no_start_optimize 禁用了跳到第一个字符的优化。 现在从“x”开始应用该模式,因此 (*COMMIT) 会导致匹配失败,而不会尝试任何其他起始点。

如果稍后匹配失败导致回溯到达该位置,则以下动词会导致匹配在主题中的当前起始位置失败

(*PRUNE) or (*PRUNE:NAME)

如果模式未锚定,则会发生正常的“bumpalong”前进到下一个起始字符。 回溯可以像往常一样发生在 (*PRUNE) 的左侧(在到达它之前),或者发生在匹配 (*PRUNE) 的右侧时,但是如果右侧没有匹配,则回溯不能越过 (*PRUNE)。 在简单的情况下,使用 (*PRUNE) 只是原子组或占有量词的替代方法,但是 (*PRUNE) 的某些用法无法以任何其他方式表达。 在锚定模式中,(*PRUNE) 与 (*COMMIT) 具有相同的效果。

(*PRUNE:NAME) 的行为与 (*MARK:NAME)(*PRUNE) 不同。 它类似于 (*MARK:NAME),因为它会记住名称以传递回调用者。 但是,(*SKIP:NAME) 仅搜索使用 (*MARK) 设置的名称。

注意

(*PRUNE:NAME) 记住名称的事实对于 Erlang 程序员来说是无用的,因为无法检索名称。

以下动词,当不指定名称时,类似于 (*PRUNE),不同之处在于,如果模式未锚定,则“bumpalong”前进不是到下一个字符,而是到主题中遇到 (*SKIP) 的位置。

(*SKIP)

(*SKIP) 表示它之前匹配的任何文本都不能成为成功匹配的一部分。 考虑

a+(*SKIP)b

如果主题是 “aaaac...”,在第一次匹配尝试失败后(从字符串中的第一个字符开始),起始点会跳过以“c”开始下一次尝试。 请注意,占有量词与此示例没有相同的效果; 尽管它会在第一次匹配尝试期间阻止回溯,但第二次尝试会从第二个字符开始,而不是跳到“c”。

当 (*SKIP) 具有关联的名称时,其行为会发生修改

(*SKIP:NAME)

当触发此操作时,会搜索模式的先前路径以查找具有相同名称的最近的 (*MARK)。 如果找到一个,则“bumpalong”前进到与该 (*MARK) 对应的主题位置,而不是到遇到 (*SKIP) 的位置。 如果没有找到具有匹配名称的 (*MARK),则忽略 (*SKIP)。

请注意,(*SKIP:NAME) 仅搜索由 (*MARK:NAME) 设置的名称。 它会忽略由 (*PRUNE:NAME) 或 (*THEN:NAME) 设置的名称。

当回溯到达以下动词时,它会导致跳到下一个最内部的选择项。 也就是说,它会取消当前选择项中的任何进一步回溯。

(*THEN) or (*THEN:NAME)

动词名称来自以下观察:它可以用于基于模式的 if-then-else 块

( COND1 (*THEN) FOO | COND2 (*THEN) BAR | COND3 (*THEN) BAZ ) ...

如果 COND1 模式匹配,则会尝试 FOO(如果 FOO 成功,可能会尝试组结束后面的其他项)。 如果失败,则匹配器会跳到第二个选择项并尝试 COND2,而不会回溯到 COND1。 如果该项成功但 BAR 失败,则会尝试 COND3。 如果 BAZ 随后失败,则没有其他选择项,因此会回溯到整个组之前的内容。 如果 (*THEN) 不在交替项内,则它的作用类似于 (*PRUNE)。

(*THEN:NAME) 的行为与 (*MARK:NAME)(*THEN) 不同。 它类似于 (*MARK:NAME),因为它会记住名称以传递回调用者。 但是,(*SKIP:NAME) 仅搜索使用 (*MARK) 设置的名称。

注意

(*THEN:NAME) 记住名称的事实对于 Erlang 程序员来说是无用的,因为无法检索名称。

不包含 | 字符的子模式只是封闭选择项的一部分;它不是只有一个选择项的嵌套交替项。 (*THEN) 的效果会扩展到这样的子模式之外,一直到封闭的选择项。 考虑以下模式,其中 A、B 等是复杂模式片段,在该级别不包含任何 | 字符

A (B(*THEN)C) | D

如果 A 和 B 匹配,但 C 中出现失败,则匹配不会回溯到 A;而是移动到下一个选择项,即 D。但是,如果包含 (*THEN) 的子模式被赋予一个选择项,它的行为会有所不同

A (B(*THEN)C | (*FAIL)) | D

(*THEN) 的效果现在仅限于内部子模式。 在 C 中失败后,匹配会移动到 (*FAIL),这会导致整个子模式失败,因为没有更多的选择项可以尝试。 在这种情况下,匹配现在确实会回溯到 A。

请注意,条件子模式不被视为具有两个选择项,因为只使用一个。 也就是说,条件子模式中的 | 字符具有不同的含义。 忽略空格,考虑

^.*? (?(?=a) a | b(*THEN)c )

如果主题是 “ba”,则此模式不匹配。 由于 .? 是非贪婪的,因此它最初匹配零个字符。 然后条件 (?=a) 失败,匹配了字符 “b”,但没有匹配 “c”。 此时,匹配不会像可能从 | 字符的存在预期的那样回溯到 .? 。 条件子模式是构成整个模式的单个选择项的一部分,因此匹配失败。(如果回溯到 .?,允许它匹配 “b”,则匹配将成功。)

当后续匹配失败时,上面描述的动词提供了四种不同的控制“强度”

  • (*THEN) 是最弱的,在下一个选择项继续匹配。
  • (*PRUNE) 紧随其后,在当前起始位置匹配失败,但允许前进到下一个字符(对于未锚定的模式)。
  • (*SKIP) 类似,不同之处在于,前进可以不止一个字符。
  • (*COMMIT) 是最强的,导致整个匹配失败。

多个回溯动词

如果模式中存在多个回溯动词,则首先回溯到的动词起作用。 例如,考虑以下模式,其中 A、B 等是复杂的模式片段

(A(*COMMIT)B(*THEN)C|ABD)

如果 A 匹配但 B 失败,则回溯到 (*COMMIT) 会导致整个匹配失败。 但是,如果 A 和 B 匹配,但 C 失败,则回溯到 (*THEN) 会导致尝试下一个选择项 (ABD)。 此行为是一致的,但并不总是与 Perl 中的行为相同。 这意味着,如果两个或多个回溯动词连续出现,则最后一个动词不起作用。 考虑以下示例

...(*COMMIT)(*PRUNE)...

如果右侧出现匹配失败,则回溯到 (*PRUNE) 会触发它,并执行其操作。 永远不可能回溯到 (*COMMIT)。

重复组中的回溯动词

PCRE 在处理重复组中的回溯动词时与 Perl 不同。 例如,考虑

/(a(*COMMIT)b)+ac/

如果主题是 “abac”,则 Perl 会匹配,但 PCRE 会失败,因为组的第二次重复中的 (*COMMIT) 会起作用。

断言中的回溯动词

断言中的 (*FAIL) 具有其正常效果:它会强制立即回溯。

正向断言中的 (*ACCEPT) 会导致断言在没有任何进一步处理的情况下成功。 在负向断言中,(*ACCEPT) 会导致断言在没有任何进一步处理的情况下失败。

如果其他回溯动词出现在正向断言中,则不会受到特殊处理。 特别是,(*THEN) 会跳到具有交替项的最内部封闭组中的下一个选择项,无论这是否在断言中。

但是,负向断言是不同的,以确保将正向断言更改为负向断言会更改其结果。 回溯到 (*COMMIT)、(*SKIP) 或 (*PRUNE) 会使负向断言为 true,而不会考虑断言中的任何其他选择分支。 回溯到 (*THEN) 会使其跳到断言内的下一个封闭选择项(正常行为),但如果断言没有这样的选择项,则 (*THEN) 的行为类似于 (*PRUNE)。

子例程中的回溯动词

无论子模式是否被递归调用,都会发生这些行为。 在某些情况下,Perl 中对子例程的处理是不同的。

  • 作为子例程调用的子模式中的 (*FAIL) 具有其正常效果:它会强制立即回溯。
  • 作为子例程调用的子模式中的 (*ACCEPT) 会导致子例程匹配在没有任何进一步处理的情况下成功。 然后在子例程调用后继续匹配。
  • 作为子例程调用的子模式中的 (*COMMIT)、(*SKIP) 和 (*PRUNE) 会导致子例程匹配失败。
  • (*THEN) 会跳到子模式内具有交替项的最内部封闭组中的下一个选择项。 如果子模式中没有这样的组,则 (*THEN) 会导致子例程匹配失败。

摘要

类型

包含已编译正则表达式的不透明数据类型。

函数

将以下描述的语法的正则表达式编译为内部格式,以供稍后用作 run/2run/3 的参数。

获取已编译的正则表达式和一个项,并返回正则表达式中的相关数据。

Subject 字符串的匹配部分替换为 Replacement

执行正则表达式匹配,并返回 match/{match, Captured}nomatch

通过根据提供的正则表达式查找标记将输入拆分为多个部分。

此函数的返回结果是一个字符串,其中包含 Erlang/OTP 编译中使用的系统的 PCRE 版本。

类型

链接到此类型

capture()

查看源 (未导出)
-type capture() ::
          all | all_but_first | all_names | first | none |
          (ValueList :: [integer() | string() | atom()]).
链接到此类型

compile_option()

查看源 (未导出)
-type compile_option() ::
          unicode | anchored | caseless | dollar_endonly | dotall | extended | firstline | multiline |
          no_auto_capture | dupnames | ungreedy |
          {newline, nl_spec()} |
          bsr_anycrlf | bsr_unicode | no_start_optimize | ucp | never_utf.
-type compile_options() :: [compile_option()].
-type mp() :: {re_pattern, _, _, _, _}.

包含已编译正则表达式的不透明数据类型。

mp/0 保证是一个元组 tuple(),并且第一个元素是原子 re_pattern,以便在保护式(guards)中进行匹配。元组的元数或其他字段的内容可能会在未来的 Erlang/OTP 版本中发生变化。

链接到此类型

nl_spec()

查看源码 (未导出)
-type nl_spec() :: cr | crlf | lf | anycrlf | any.
链接到此类型

option()

查看源码 (未导出)
-type option() ::
          anchored | global | notbol | noteol | notempty | notempty_atstart | report_errors |
          {offset, non_neg_integer()} |
          {match_limit, non_neg_integer()} |
          {match_limit_recursion, non_neg_integer()} |
          {newline, NLSpec :: nl_spec()} |
          bsr_anycrlf | bsr_unicode |
          {capture, ValueSpec :: capture()} |
          {capture, ValueSpec :: capture(), Type :: index | list | binary} |
          compile_option().
-type options() :: [option()].
链接到此类型

replace_fun()

查看源码 (未导出)
-type replace_fun() :: fun((binary(), [binary()]) -> iodata() | unicode:charlist()).

函数

-spec compile(Regexp) -> {ok, MP} | {error, ErrSpec}
                 when
                     Regexp :: iodata(),
                     MP :: mp(),
                     ErrSpec :: {ErrString :: string(), Position :: non_neg_integer()}.

compile(Regexp,[]) 相同

链接到此函数

compile(Regexp, Options)

查看源
-spec compile(Regexp, Options) -> {ok, MP} | {error, ErrSpec}
                 when
                     Regexp :: iodata() | unicode:charlist(),
                     Options :: [Option],
                     Option :: compile_option(),
                     MP :: mp(),
                     ErrSpec :: {ErrString :: string(), Position :: non_neg_integer()}.

将以下描述的语法的正则表达式编译为内部格式,以供稍后用作 run/2run/3 的参数。

如果相同的正则表达式需要在程序的生命周期内多次用于匹配多个主题,则在匹配之前编译正则表达式非常有用。编译一次并执行多次比每次要匹配时都编译要高效得多。

当指定选项 unicode 时,正则表达式应指定为有效的 Unicode charlist(),否则应指定为任何有效的 iodata/0

选项

  • unicode - 正则表达式指定为 Unicode charlist(),并且生成的正则表达式代码将针对有效的 Unicode charlist() 主题运行。使用 Unicode 字符时,也请考虑选项 ucp

  • anchored - 强制模式为“锚定”模式,也就是说,它被限制为仅在被搜索的字符串(“主题字符串”)中的第一个匹配点进行匹配。这种效果也可以通过模式本身中的适当构造来实现。

  • caseless - 模式中的字母匹配大写和小写字母。它等效于 Perl 选项 /i,并且可以在模式内通过 (?i) 选项设置进行更改。大写和小写字母的定义与 ISO 8859-1 字符集中定义的一致。

  • dollar_endonly - 模式中的美元元字符仅在主题字符串的末尾匹配。如果没有此选项,美元也会在字符串末尾的换行符之前立即匹配(但不会在任何其他换行符之前匹配)。如果指定了选项 multiline,则忽略此选项。在 Perl 中没有等效的选项,并且无法在模式内设置。

  • dotall - 模式中的点匹配所有字符,包括指示换行的字符。如果没有此选项,当当前位置位于换行符时,点不会匹配。此选项等效于 Perl 选项 /s,并且可以在模式内通过 (?s) 选项设置进行更改。负类(例如 [^a])始终匹配换行符,而与此选项的设置无关。

  • extended - 如果设置了此选项,则模式中的大多数空格字符将被完全忽略,除非它们被转义或在字符类内部。但是,不允许在诸如 (?> 之类的引入各种带括号的子模式的序列中,也不允许在诸如 {1,3} 之类的数字量词中出现空格。但是,允许在项和后面的量词之间,以及在量词和后面的指示所有格的 + 之间出现可忽略的空格。

    空格以前不包括 VT 字符(代码 11),因为 Perl 不将此字符视为空格。但是,Perl 在 5.18 版本中进行了更改,因此 PCRE 在 8.34 版本中也进行了更改,VT 现在被视为空格。

    这还会导致字符类外部未转义的 # 字符和下一个换行符之间的字符(包括换行符)被忽略。这等效于 Perl 的 /x 选项,并且可以在模式内通过 (?x) 选项设置进行更改。

    使用此选项,可以在复杂的模式中包含注释。但是,请注意,这仅适用于数据字符。空格字符永远不会出现在模式中的特殊字符序列中,例如,在引入条件子模式的序列 (?( 中。

  • firstline - 要求未锚定的模式在主题字符串中的第一个换行符之前或在该换行符处匹配,尽管匹配的文本可以继续跨越该换行符。

  • multiline - 默认情况下,PCRE 将主题字符串视为由单行字符组成(即使它包含换行符)。“行首”元字符 (^) 仅在字符串的开头匹配,而“行尾”元字符 ($) 仅在字符串的末尾或在终止换行符之前匹配(除非指定了选项 dollar_endonly)。这与 Perl 中的情况相同。

    指定此选项时,“行首”和“行尾”构造分别在主题字符串中的内部换行符之后或之前立即匹配,以及在最开始和结尾处匹配。这等效于 Perl 选项 /m,并且可以在模式内通过 (?m) 选项设置进行更改。如果主题字符串中没有换行符,或者模式中没有 ^$ 的出现,则设置 multiline 不会产生任何影响。

  • no_auto_capture - 禁用在模式中使用带编号的捕获括号。任何不跟在 ? 后面的左括号的行为都好像它后面跟了 ?:。命名括号仍可用于捕获(并且它们以通常的方式获取数字)。在 Perl 中没有等效的选项。

  • dupnames - 用于标识捕获子模式的名称不需要是唯一的。当已知命名子模式的只有一个实例可以被匹配时,这对某些类型的模式很有帮助。下面提供了有关命名子模式的更多详细信息。

  • ungreedy - 反转量词的“贪婪性”,使其默认情况下不是贪婪的,但如果后面跟有“?”,则变为贪婪的。它与 Perl 不兼容。也可以通过模式内的 (?U) 选项设置来设置。

  • {newline, NLSpec} - 覆盖主题字符串中换行符的默认定义,该定义在 Erlang 中为 LF (ASCII 10)。

    • cr - 换行符由单个字符 cr (ASCII 13) 表示。

    • lf - 换行符由单个字符 LF (ASCII 10) 表示,这是默认设置。

    • crlf - 换行符由双字符 CRLF(ASCII 13 后跟 ASCII 10)序列表示。

    • anycrlf - 将识别上述三个序列中的任何一个。

    • any - 上述任何换行符序列,以及 Unicode 序列 VT(垂直制表符,U+000B)、FF(换页符,U+000C)、NEL(下一行,U+0085)、LS(行分隔符,U+2028)和 PS(段落分隔符,U+2029)。

  • bsr_anycrlf - 特别指定 \R 仅匹配 CR、LF 或 CRLF 序列,而不匹配特定于 Unicode 的换行符。

  • bsr_unicode - 特别指定 \R 匹配所有 Unicode 换行符(包括 CRLF 等,这是默认设置)。

  • no_start_optimize - 禁用如果正则表达式中存在“特殊模式开始项”时可能发生故障的优化。一个典型的例子是将 "DEFABC" 与 "(COMMIT)ABC" 进行匹配,其中 PCRE 的起始优化会将主题跳过到 "A",并且永远不会意识到 (COMMIT) 指令应该导致匹配失败。仅当您使用“模式开始项”时,此选项才相关,如 PCRE 正则表达式详细信息部分所述。

  • ucp - 指定在解析 \B、\b、\D、\d、\S、\s、\W 和 \w 时使用 Unicode 字符属性。如果没有此标志,则仅使用 ISO Latin-1 属性。使用 Unicode 属性会损害性能,但是在处理超出 ISO Latin-1 范围的 Unicode 字符时,在语义上是正确的。

  • never_utf - 指定禁止 (UTF) 和/或 (UTF8)“模式开始项”。此标志不能与选项 unicode 组合使用。如果要编译来自外部源的 ISO Latin-1 模式,则此选项很有用。

链接到此函数

inspect(MP, Item)

查看源码 (自 OTP 17.0 起)
-spec inspect(MP, Item) -> {namelist, [binary()]} when MP :: mp(), Item :: namelist.

获取已编译的正则表达式和一个项,并返回正则表达式中的相关数据。

唯一支持的项是 namelist,它返回元组 {namelist, [binary()]},其中包含正则表达式中所有(唯一)命名子模式的名称。

例如

1> {ok,MP} = re:compile("(?<A>A)|(?<B>B)|(?<C>C)").
{ok,{re_pattern,3,0,0,
                <<69,82,67,80,119,0,0,0,0,0,0,0,1,0,0,0,255,255,255,255,
                  255,255,...>>}}
2> re:inspect(MP,namelist).
{namelist,[<<"A">>,<<"B">>,<<"C">>]}
3> {ok,MPD} = re:compile("(?<C>A)|(?<B>B)|(?<C>C)",[dupnames]).
{ok,{re_pattern,3,0,0,
                <<69,82,67,80,119,0,0,0,0,0,8,0,1,0,0,0,255,255,255,255,
                  255,255,...>>}}
4> re:inspect(MPD,namelist).
{namelist,[<<"B">>,<<"C">>]}

在第二个示例中,请注意重复的名称仅在返回的列表中出现一次,并且列表是按字母顺序排列的,而与名称在正则表达式中的位置无关。如果将 {capture, all_names} 指定为 run/3 的选项,则名称的顺序与捕获的子表达式的顺序相同。因此,您可以从 run/3 的结果创建名称到值的映射,如下所示

1> {ok,MP} = re:compile("(?<A>A)|(?<B>B)|(?<C>C)").
{ok,{re_pattern,3,0,0,
                <<69,82,67,80,119,0,0,0,0,0,0,0,1,0,0,0,255,255,255,255,
                  255,255,...>>}}
2> {namelist, N} = re:inspect(MP,namelist).
{namelist,[<<"A">>,<<"B">>,<<"C">>]}
3> {match,L} = re:run("AA",MP,[{capture,all_names,binary}]).
{match,[<<"A">>,<<>>,<<>>]}
4> NameMap = lists:zip(N,L).
[{<<"A">>,<<"A">>},{<<"B">>,<<>>},{<<"C">>,<<>>}]
链接到此函数

replace(Subject, RE, Replacement)

查看源
-spec replace(Subject, RE, Replacement) -> iodata() | unicode:charlist()
                 when
                     Subject :: iodata() | unicode:charlist(),
                     RE :: mp() | iodata(),
                     Replacement :: iodata() | unicode:charlist() | replace_fun().

等效于 replace(Subject, RE, Replacement, [])

链接到此函数

replace(Subject, RE, Replacement, Options)

查看源
-spec replace(Subject, RE, Replacement, Options) -> iodata() | unicode:charlist()
                 when
                     Subject :: iodata() | unicode:charlist(),
                     RE :: mp() | iodata() | unicode:charlist(),
                     Replacement :: iodata() | unicode:charlist() | replace_fun(),
                     Options :: [Option],
                     Option ::
                         anchored | global | notbol | noteol | notempty | notempty_atstart |
                         {offset, non_neg_integer()} |
                         {newline, NLSpec} |
                         bsr_anycrlf |
                         {match_limit, non_neg_integer()} |
                         {match_limit_recursion, non_neg_integer()} |
                         bsr_unicode |
                         {return, ReturnType} |
                         CompileOpt,
                     ReturnType :: iodata | list | binary,
                     CompileOpt :: compile_option(),
                     NLSpec :: cr | crlf | lf | anycrlf | any.

Subject 字符串的匹配部分替换为 Replacement

允许的选项与 run/3 的选项相同,除了不允许选项 capture。而是存在 {return, ReturnType}。默认返回类型为 iodata,其构造方式旨在最大限度地减少复制。 iodata 结果可以直接在许多 I/O 操作中使用。如果需要平面的 list/0,请指定 {return, list}。如果需要二进制数据,请指定 {return, binary}

如同函数 run/3 中一样,使用选项 unicode 编译的 mp/0 要求 Subject 必须是 Unicode charlist()。 如果编译是隐式进行的,并且此函数指定了 unicode 编译选项,则正则表达式和 Subject 都必须指定为有效的 Unicode charlist()

如果替换项是字符串,则它可以包含特殊字符 &,该字符将整个匹配的表达式插入结果中,以及特殊序列 \N (其中 N 是大于 0 的整数), \gN 或 \g{N},这将导致子表达式编号 N 被插入到结果中。如果正则表达式未生成具有该编号的子表达式,则不会插入任何内容。

要在结果中插入 & 或 \,请在其前面添加 \。 请注意,Erlang 已经为文字字符串中的 \ 赋予了特殊含义,因此单个 \ 必须写为 "\\",因此双 \ 必须写为 "\\\\"

示例

1> re:replace("abcd","c","[&]",[{return,list}]).
"ab[c]d"

2> re:replace("abcd","c","[\\&]",[{return,list}]).
"ab[&]d"

如果替换项是函数,则将使用整个匹配的表达式作为第一个参数以及按照它们在正则表达式中出现的顺序排列的子表达式匹配列表来调用它。 返回的值将插入到结果中。

示例

3> re:replace("abcd", ".(.)",
    fun(Whole, [<<C>>]) ->
         <<$#, Whole/binary, $-, (C - $a + $A), $#>>
    end,
    [{return, list}]).
"#ab-B#cd"

注意

如果非匹配的可选子表达式是正则表达式中的最后一个子表达式,则它们将不会包含在子表达式匹配列表中。

示例

正则表达式 "(a)(b)?(c)?" (“a”,可选地后跟“b”,可选地后跟“c”)将创建以下子表达式列表

  • [<<"a">>, <<"b">>, <<"c">>] 应用于字符串 "abc"
  • [<<"a">>, <<>>, <<"c">>] 应用于字符串 "acx"
  • [<<"a">>, <<"b">>] 应用于字符串 "abx"
  • [<<"a">>] 应用于字符串 "axx"

run/3 一样,编译错误会引发 badarg 异常。 compile/2 可用于获取有关错误的更多信息。

-spec run(Subject, RE) -> {match, Captured} | nomatch
             when
                 Subject :: iodata() | unicode:charlist(),
                 RE :: mp() | iodata(),
                 Captured :: [CaptureData],
                 CaptureData :: {integer(), integer()}.

等效于 run(Subject, RE, [])

链接到此函数

run(Subject, RE, Options)

查看源
-spec run(Subject, RE, Options) -> {match, Captured} | match | nomatch | {error, ErrType}
             when
                 Subject :: iodata() | unicode:charlist(),
                 RE :: mp() | iodata() | unicode:charlist(),
                 Options :: options(),
                 Captured :: [CaptureData] | [[CaptureData]],
                 CaptureData :: {integer(), integer()} | ListConversionData | binary(),
                 ListConversionData ::
                     string() | {error, string(), binary()} | {incomplete, string(), binary()},
                 ErrType :: match_limit | match_limit_recursion | {compile, CompileErr},
                 CompileErr :: {ErrString :: string(), Position :: non_neg_integer()}.

执行正则表达式匹配,并返回 match/{match, Captured}nomatch

正则表达式可以指定为 iodata/0,在这种情况下它会自动编译(如通过 compile/2)并执行,或者指定为预编译的 mp/0,在这种情况下它将直接针对 subject 执行。

当涉及到编译时,如果发生编译错误,则会抛出异常 badarg。 调用 compile/2 以获取有关正则表达式中错误位置的信息。

如果正则表达式是先前编译的,则选项列表只能包含以下选项

  • anchored
  • {capture, ValueSpec}/{capture, ValueSpec, Type}
  • global
  • {match_limit, integer() >= 0}
  • {match_limit_recursion, integer() >= 0}
  • {newline, NLSpec}
  • notbol
  • notempty
  • notempty_atstart
  • noteol
  • {offset, integer() >= 0}
  • report_errors

否则,函数 compile/2 的所有有效选项也都允许。 允许用于编译和执行匹配的选项,即 anchored{newline, NLSpec},如果与非预编译的正则表达式一起存在,则会同时影响编译和执行。

如果正则表达式先前使用选项 unicode 编译,则 Subject 必须作为有效的 Unicode charlist() 提供,否则任何 iodata/0 都可以。 如果涉及到编译并且指定了选项 unicode,则 Subject 和正则表达式都必须指定为有效的 Unicode charlists()

{capture, ValueSpec}/{capture, ValueSpec, Type} 定义成功匹配后从函数返回的内容。 capture 元组可以包含一个值规范,说明要返回哪些捕获的子字符串,以及一个类型规范,说明如何返回捕获的子字符串(作为索引元组、列表或二进制文件)。 下面将详细描述这些选项。

如果捕获选项描述不执行任何子字符串捕获({capture, none}),则该函数在成功匹配后返回单个原子 match,否则返回元组 {match, ValueList}。 可以通过指定 none 或空列表作为 ValueSpec 来禁用捕获。

选项 report_errors 添加了返回错误元组的可能性。 该元组要么指示匹配错误(match_limitmatch_limit_recursion),要么指示编译错误,其中错误元组的格式为 {error, {compile, CompileErr}}。 请注意,如果未指定选项 report_errors,则该函数永远不会返回错误元组,而是将编译错误报告为 badarg 异常,并将由于超出匹配限制而失败的匹配简单地报告为 nomatch

以下选项与执行相关

  • anchored - 将 run/3 限制为在第一个匹配位置进行匹配。 如果模式是用 anchored 编译的,或者因其内容而变为锚定的,则在匹配时无法将其取消锚定,因此没有 unanchored 选项。

  • global - 实现全局(重复)搜索(Perl 中的标志 g)。 每个匹配都作为单独的 list/0 返回,其中包含特定的匹配和任何匹配的子表达式(或如选项 capture 所指定)。 因此,当指定此选项时,返回值的 Captured 部分是 list/0list/0

    选项 global 与匹配空字符串的正则表达式的交互使某些用户感到惊讶。 当指定选项 global 时,run/3 处理空匹配的方式与 Perl 相同:在任何位置的零长度匹配也会使用选项 [anchored, notempty_atstart] 重试。 如果该搜索给出的结果长度 > 0,则该结果将包含在内。 示例

    re:run("cat","(|at)",[global]).

    执行以下匹配

    • 在偏移量 0 - 正则表达式 (|at) 首先在字符串 cat 的初始位置匹配,给出结果集 [{0,0},{0,0}](第二个 {0,0} 是因为括号标记的子表达式)。 由于匹配的长度为 0,我们不会立即前进到下一个位置。

    • 在偏移量 0 处,使用 [anchored, notempty_atstart] - 使用选项 [anchored, notempty_atstart] 在同一位置重试搜索,这不会给出任何更长的有趣结果,因此搜索位置前进到下一个字符(a)。

    • 在偏移量 1 - 搜索结果为 [{1,0},{1,0}],因此也会使用额外的选项重复此搜索。

    • 在偏移量 1 处,使用 [anchored, notempty_atstart] - 找到替代项 ab,结果为 [{1,2},{1,2}]。 该结果将添加到结果列表中,并且在搜索字符串中的位置前进两步。

    • 在偏移量 3 - 搜索再次匹配空字符串,给出 [{3,0},{3,0}]

    • 在偏移量 1 处,使用 [anchored, notempty_atstart] - 这不会给出任何长度 > 0 的结果,并且我们处于最后一个位置,因此全局搜索完成。

    调用的结果是

    {match,[[{0,0},{0,0}],[{1,0},{1,0}],[{1,2},{1,2}],[{3,0},{3,0}]]}
  • notempty - 如果指定此选项,则空字符串不被视为有效的匹配项。 如果模式中存在备选项,则会尝试它们。 如果所有备选项都匹配空字符串,则整个匹配失败。

    示例

    如果将以下模式应用于不以“a”或“b”开头的字符串,它通常会在 subject 的开头匹配空字符串

    a?b?

    使用选项 notempty,此匹配无效,因此 run/3 会进一步搜索字符串中出现的“a”或“b”。

  • notempty_atstart - 类似于 notempty,不同之处在于允许不位于 subject 开头的空字符串匹配。 如果模式是锚定的,则只有当模式包含 \K 时才会发生此类匹配。

    Perl 没有 notemptynotempty_atstart 的直接等效项,但它确实在其 split() 函数中以及使用修饰符 /g 时对空字符串的模式匹配进行了特殊处理。 可以通过在匹配空字符串后,先尝试使用 notempty_atstartanchored 在同一偏移量再次匹配,然后,如果失败,则通过前进起始偏移量(如下所示)并再次尝试普通匹配来模拟 Perl 行为。

  • notbol - 指定 subject 字符串的第一个字符不是行的开头,因此脱字符元字符不应在其之前匹配。 在没有 multiline(在编译时)的情况下设置此选项会导致脱字符永远不匹配。 此选项仅影响脱字符元字符的行为。 它不会影响 \A。

  • noteol - 指定 subject 字符串的结尾不是行的结尾,因此美元元字符不应匹配它,也不应(多行模式除外)匹配它之前紧接的新行。 在没有 multiline(在编译时)的情况下设置此选项会导致美元符号永远不匹配。 此选项仅影响美元元字符的行为。 它不会影响 \Z 或 \z。

  • report_errors - 可以更好地控制 run/3 中的错误处理。 指定后,编译错误(如果正则表达式尚未编译)和运行时错误将显式返回为错误元组。

    以下是可能的运行时错误

    • match_limit - PCRE 库对内部匹配函数可以调用的次数设置了限制。 在为 Erlang 编译的库中,默认值为 10,000,000。 如果返回 {error, match_limit},则表示正则表达式的执行已达到此限制。 这通常被视为 nomatch,这是发生这种情况时的默认返回值,但是通过指定 report_errors,您可以获知匹配由于内部调用次数过多而失败。

    • match_limit_recursion - 此错误与 match_limit 非常相似,但当 PCRE 的内部匹配函数被“递归”调用次数超过 match_limit_recursion 限制时会发生此错误,该限制也默认为 10,000,000。 请注意,只要 match_limitmatch_limit_default 值保持默认值,就不会发生 match_limit_recursion 错误,因为 match_limit 错误会在其之前发生(每次递归调用也是一次调用,但反之则不然)。 但是,可以通过直接在正则表达式字符串中设置限制(请参阅PCRE 正则表达式详细信息部分)或通过为 run/3 指定选项来更改这两个限制。

    重要的是要理解,在限制匹配时所指的“递归”并非 Erlang 虚拟机的 C 堆栈或 Erlang 进程堆栈上的递归。编译到 Erlang 虚拟机中的 PCRE 版本使用机器“堆”内存来存储在正则表达式匹配中必须保留的递归值。

  • {match_limit, integer() >= 0} - 以特定于实现的方式限制匹配的执行时间。PCRE 文档对其描述如下:

    match_limit 字段提供了一种方法,以防止 PCRE 在运行不会匹配的模式时耗尽大量资源,这些模式在其搜索树中具有非常多的可能性。典型的例子是使用嵌套的无限制重复的模式。

    在内部,pcre_exec() 使用一个名为 match() 的函数,它会重复(有时是递归地)调用该函数。match_limit 设置的限制是强加于匹配期间此函数被调用的次数,这具有限制可能发生的回溯量的效果。对于未锚定的模式,计数对于主题字符串中的每个位置都从零开始。

    这意味着如果使用此选项降低限制,则失控的正则表达式匹配可以更快地失败。默认值 10,000,000 已编译到 Erlang 虚拟机中。

    注意

    此选项绝不会影响 Erlang 虚拟机在“长时间运行的 BIF”方面的执行。run/3 始终会在间隔时间将控制权返回给 Erlang 进程的调度器,从而确保 Erlang 系统的实时属性。

  • {match_limit_recursion, integer() >= 0} - 以特定于实现的方式限制匹配的执行时间和内存消耗,与 match_limit 非常相似。PCRE 文档对其描述如下:

    match_limit_recursion 字段与 match_limit 类似,但它不是限制 match() 被调用的总次数,而是限制递归的深度。递归深度是一个小于总调用次数的数字,因为并非所有对 match() 的调用都是递归的。只有在将其设置得小于 match_limit 时,此限制才有用。

    限制递归深度会限制可以使用的机器堆栈量,或者,当 PCRE 被编译为在堆上而不是堆栈上使用内存时,会限制可以使用的堆内存量。

    Erlang 虚拟机使用 PCRE 库,其中当发生正则表达式匹配递归时,会使用堆内存。因此,这限制了机器堆的使用,而不是 C 堆栈。

    指定较低的值可能会导致具有深度递归的匹配失败,即使它们应该匹配。

    1> re:run("aaaaaaaaaaaaaz","(a+)*z").
    {match,[{0,14},{0,13}]}
    2> re:run("aaaaaaaaaaaaaz","(a+)*z",[{match_limit_recursion,5}]).
    nomatch
    3> re:run("aaaaaaaaaaaaaz","(a+)*z",[{match_limit_recursion,5},report_errors]).
    {error,match_limit_recursion}

    此选项和选项 match_limit 仅在极少数情况下使用。建议在修改这些限制之前,了解 PCRE 库的内部原理。

  • {offset, integer() >= 0} - 从主题字符串中指定的偏移量(位置)开始匹配。偏移量从零开始,因此默认值为 {offset,0}(整个主题字符串)。

  • {newline, NLSpec} - 覆盖主题字符串中换行符的默认定义,该定义在 Erlang 中为 LF (ASCII 10)。

    • cr - 换行符由单个字符 CR (ASCII 13) 表示。

    • lf - 换行符由单个字符 LF (ASCII 10) 表示,这是默认设置。

    • crlf - 换行符由双字符 CRLF(ASCII 13 后跟 ASCII 10)序列表示。

    • anycrlf - 将识别以上三个序列中的任何一个。

    • any - 上述任何换行符序列,以及 Unicode 序列 VT(垂直制表符,U+000B)、FF(换页符,U+000C)、NEL(下一行,U+0085)、LS(行分隔符,U+2028)和 PS(段落分隔符,U+2029)。

  • bsr_anycrlf - 专门指定 \R 仅匹配 CR LF 或 CRLF 序列,而不匹配特定于 Unicode 的换行符。(覆盖编译选项。)

  • bsr_unicode - 专门指定 \R 匹配所有 Unicode 换行符(包括 CRLF 等,默认值)。(覆盖编译选项。)

  • {capture, ValueSpec}/{capture, ValueSpec, Type} - 指定返回哪些捕获的子字符串以及采用何种格式。默认情况下,run/3 捕获子字符串的匹配部分和所有捕获的子模式(自动捕获所有模式)。默认返回类型是字符串捕获部分的(从零开始的)索引,指定为 {Offset,Length} 对(捕获的 index Type)。

    作为默认行为的示例,以下调用返回,作为第一个也是唯一捕获的字符串,主题的匹配部分(中间的“abcd”)作为索引对 {3,4},其中字符位置从零开始,就像偏移量一样。

    re:run("ABCabcdABC","abcd",[]).

    此调用的返回值是:

    {match,[{3,4}]}

    另一种(很常见)情况是正则表达式匹配所有主题:

    re:run("ABCabcdABC",".*abcd.*",[]).

    这里返回值相应地指出了整个字符串,从索引 0 开始,长度为 10 个字符。

    {match,[{0,10}]}

    如果正则表达式包含捕获子模式,例如在:

    re:run("ABCabcdABC",".*(abcd).*",[]).

    则捕获所有匹配的主题以及捕获的子字符串。

    {match,[{0,10},{3,4}]}

    完整的匹配模式始终给出列表中的第一个返回值,其余子模式按照它们在正则表达式中出现的顺序添加。

    捕获元组构建如下:

    • ValueSpec - 指定要返回哪些捕获的(子)模式。 ValueSpec 可以是描述一组预定义返回值 的原子,也可以是包含要返回的特定子模式的索引或名称的列表。

      以下是预定义的子模式集:

      • all - 所有捕获的子模式,包括完整的匹配字符串。这是默认值。

      • all_names - 正则表达式中所有命名的子模式,就像指定了一个所有名称的 list/0按字母顺序排列)一样。也可以使用 inspect/2 检索所有名称的列表。

      • first - 仅第一个捕获的子模式,它始终是主题的完整匹配部分。所有显式捕获的子模式都将被丢弃。

      • all_but_first - 除了第一个匹配的子模式之外的所有子模式,即所有显式捕获的子模式,但不包括主题字符串的完整匹配部分。如果正则表达式作为一个整体匹配主题的很大一部分,但你感兴趣的部分在显式捕获的子模式中,这将很有用。如果返回类型是 listbinary,则不返回你不感兴趣的子模式是优化的一种好方法。

      • none - 不返回任何匹配的子模式,如果匹配成功,则返回单个原子 match 作为函数的返回值,而不是返回 {match, list()}。指定一个空列表会产生相同的行为。

      值列表是要返回的子模式的索引列表,其中索引 0 用于所有模式,1 用于正则表达式中的第一个显式捕获子模式,依此类推。当在正则表达式中使用命名捕获子模式(请参见下文)时,可以使用 atom/0string/0 指定要返回的子模式。例如,考虑正则表达式:

      ".*(abcd).*"

      针对字符串 "ABCabcdABC" 进行匹配,仅捕获 "abcd" 部分(第一个显式子模式):

      re:run("ABCabcdABC",".*(abcd).*",[{capture,[1]}]).

      此调用返回以下结果,因为第一个显式捕获的子模式是 "(abcd)",在主题中匹配 "abcd",位于(从零开始的)位置 3,长度为 4:

      {match,[{3,4}]}

      考虑相同的正则表达式,但子模式显式命名为 'FOO':

      ".*(?<FOO>abcd).*"

      使用此表达式,我们仍然可以使用以下调用给出子模式的索引:

      re:run("ABCabcdABC",".*(?<FOO>abcd).*",[{capture,[1]}]).

      给出与之前相同的结果。但是,由于子模式已命名,我们也可以在值列表中指定其名称:

      re:run("ABCabcdABC",".*(?<FOO>abcd).*",[{capture,['FOO']}]).

      这将给出与早期示例相同的结果,即:

      {match,[{3,4}]}

      值列表可以指定正则表达式中不存在的索引或名称,在这种情况下,返回值会根据类型而变化。如果类型为 index,则对于正则表达式中没有相应子模式的值,将返回元组 {-1,0},但对于其他类型(binarylist),值分别是空二进制或列表。

    • Type - 可选地指定如何返回捕获的子字符串。如果省略,则使用默认的 index

      Type 可以是以下之一:

      • index - 将捕获的子字符串作为字节索引对返回到主题字符串中,并返回主题中匹配字符串的长度(就像在匹配之前使用 erlang:iolist_to_binary/1unicode:characters_to_binary/2 将主题字符串展平一样)。请注意,选项 unicode 会生成(可能虚拟的)UTF-8 编码二进制中的面向字节的索引。因此,当 unicode 生效时,字节索引元组 {0,2} 可以表示一个或两个字符。这似乎有悖常理,但已被认为是实现它的最有效和最有用的方式。如果需要,返回列表可以简化代码。此返回类型是默认类型。

      • list - 将匹配的子字符串作为字符列表(Erlang string/0)返回。如果选项 unicode 与正则表达式中的 \C 序列结合使用,则捕获的子模式可能包含不是有效的 UTF-8 的字节(\C 匹配字节,无论字符编码如何)。在这种情况下,list 捕获可以产生 unicode:characters_to_list/2 可以返回的相同类型的元组,即具有标记 incompleteerror 的三元组、成功转换的字符和转换的无效 UTF-8 尾部(作为二进制)。最好的策略是避免在捕获列表时使用 \C 序列。

      • binary - 将匹配的子字符串作为二进制文件返回。如果使用选项 unicode,则这些二进制文件采用 UTF-8 格式。如果将 \C 序列与 unicode 一起使用,则二进制文件可能是无效的 UTF-8。

    通常,当 typeindex 时,在匹配中未赋值的子模式会以元组 {-1,0} 的形式返回。对于其他返回类型,未赋值的子模式分别以空二进制或列表的形式返回。考虑以下正则表达式:

    ".*((?<FOO>abdd)|a(..d)).*"

    这里有三个显式捕获的子模式,开括号的位置决定了结果中的顺序。因此,((?<FOO>abdd)|a(..d)) 的子模式索引为 1,(?<FOO>abdd) 的子模式索引为 2,而 (..d) 的子模式索引为 3。当与以下字符串匹配时:

    "ABCabcdABC"

    索引为 2 的子模式不匹配,因为字符串中不存在 “abdd”,但是整个模式匹配(因为备选的 a(..d))。因此,索引为 2 的子模式未被赋值,默认的返回值是:

    {match,[{0,10},{3,4},{-1,0},{4,3}]}

    将捕获的 Type 设置为 binary,则返回:

    {match,[<<"ABCabcdABC">>,<<"abcd">>,<<>>,<<"bcd">>]}

    这里,空二进制 (<<>>) 表示未赋值的子模式。在 binary 情况下,关于匹配的一些信息会丢失,因为 <<>> 也可以表示捕获的空字符串。

    如果需要区分空匹配和不存在的子模式,请使用 type index,并在 Erlang 代码中进行到最终类型的转换。

    当指定 global 选项时,capture 规范会分别影响每个匹配,因此:

    re:run("cacb","c(a|b)",[global,{capture,[1],list}]).

    会得到:

    {match,[["a"],["b"]]}

有关仅影响编译步骤的选项的描述,请参阅 compile/2

链接到此函数

split(Subject, RE)

查看源
-spec split(Subject, RE) -> SplitList
               when
                   Subject :: iodata() | unicode:charlist(),
                   RE :: mp() | iodata(),
                   SplitList :: [iodata() | unicode:charlist()].

等效于 split(Subject, RE, [])

链接到此函数

split(Subject, RE, Options)

查看源
-spec split(Subject, RE, Options) -> SplitList
               when
                   Subject :: iodata() | unicode:charlist(),
                   RE :: mp() | iodata() | unicode:charlist(),
                   Options :: [Option],
                   Option ::
                       anchored | notbol | noteol | notempty | notempty_atstart |
                       {offset, non_neg_integer()} |
                       {newline, nl_spec()} |
                       {match_limit, non_neg_integer()} |
                       {match_limit_recursion, non_neg_integer()} |
                       bsr_anycrlf | bsr_unicode |
                       {return, ReturnType} |
                       {parts, NumParts} |
                       group | trim | CompileOpt,
                   NumParts :: non_neg_integer() | infinity,
                   ReturnType :: iodata | list | binary,
                   CompileOpt :: compile_option(),
                   SplitList :: [RetData] | [GroupedRetData],
                   GroupedRetData :: [RetData],
                   RetData :: iodata() | unicode:charlist() | binary() | list().

通过根据提供的正则表达式查找标记将输入拆分为多个部分。

拆分基本上是通过运行全局正则表达式匹配,并在每次发生匹配时分割初始字符串来完成的。字符串的匹配部分会从输出中移除。

run/3 中一样,使用 unicode 选项编译的 mp/0 要求 Subject 是 Unicode charlist()。如果隐式完成编译并且向此函数指定了 unicode 编译选项,则正则表达式和 Subject 都必须指定为有效的 Unicode charlist()

结果以“字符串”列表的形式给出,首选的数据类型在选项 return 中指定(默认为 iodata)。

如果在正则表达式中指定了子表达式,则匹配的子表达式也会在结果列表中返回。例如:

re:split("Erlang","[ln]",[{return,list}]).

会得到:

["Er","a","g"]

re:split("Erlang","([ln])",[{return,list}]).

会得到:

["Er","l","a","n","g"]

与子表达式匹配的文本(在正则表达式中由括号标记)将插入到结果列表中,其位置就是它被找到的位置。这意味着,拆分的结果中,如果整个正则表达式是单个子表达式(如最后一个示例中所示),则将结果连接起来总是会得到原始字符串。

由于示例的最后一部分(“g”)没有匹配的子表达式,因此之后不会插入任何内容。为了使字符串组和匹配子表达式的部分更加明显,可以使用选项 group,它将主题字符串的部分与字符串被拆分时与子表达式匹配的部分组合在一起:

re:split("Erlang","([ln])",[{return,list},group]).

会得到:

[["Er","l"],["a","n"],["g"]]

这里,正则表达式首先匹配 “l”,导致 “Er” 成为结果中的第一部分。当正则表达式匹配时,(唯一)子表达式绑定到 “l”,因此 “l” 与 “Er” 一起插入到组中。下一个匹配是 “n”,使 “a” 成为下一个要返回的部分。在这种情况下,由于子表达式绑定到子字符串 “n”,因此 “n” 会被插入到此组中。最后一组包含剩余的字符串,因为没有找到更多匹配项。

默认情况下,字符串的所有部分,包括空字符串,都会从函数返回,例如:

re:split("Erlang","[lg]",[{return,list}]).

会得到:

["Er","an",[]]

由于字符串末尾的 “g” 匹配后会留下一个空的剩余部分,该部分也会返回。此行为与 Perl 中 split 函数的默认行为不同,在 Perl 中,末尾的空字符串默认会被删除。要获得 Perl 的 “修剪” 默认行为,请将 trim 指定为选项:

re:split("Erlang","[lg]",[{return,list},trim]).

会得到:

["Er","an"]

“trim” 选项表示:“给我尽可能多的部分,但排除空部分”,这有时很有用。你也可以通过指定 {parts,N} 来指定你想要多少部分:

re:split("Erlang","[lg]",[{return,list},{parts,2}]).

会得到:

["Er","ang"]

请注意,最后一部分是 “ang”,而不是 “an”,因为拆分被指定为两部分,并且当给出足够的部分时拆分会停止,这就是为什么结果与 trim 的结果不同的原因。

使用此输入数据不可能拆分为三个以上的部分,因此:

re:split("Erlang","[lg]",[{return,list},{parts,4}]).

与默认值给出相同的结果,该默认值应被视为“无限数量的部分”。

指定 0 作为部分数量会产生与选项 trim 相同的效果。如果捕获了子表达式,则如果指定了 trim{parts,0},则从结果中也会删除末尾匹配的空子表达式。

trim 行为与 Perl 默认行为完全对应。{parts,N},其中 N 是一个正整数,与 Perl 使用正数值作为第三个参数的行为完全对应。split/3 的默认行为与 Perl 例程指定负整数作为第三个参数时的行为相对应。

函数 run/3 中之前未描述的选项摘要:

  • {return,ReturnType} - 指定原始字符串的部分如何在结果列表中呈现。有效类型:

    • iodata - iodata/0 的变体,在当前实现中数据复制最少(通常是二进制,但不要依赖它)。

    • binary - 所有部分都以二进制形式返回。

    • list - 所有部分都以字符列表(“字符串”)的形式返回。

  • group - 将字符串的一部分与正则表达式的子表达式匹配的字符串部分组合在一起。

    在这种情况下,函数的返回值是一个 list/0list/0。每个子列表都以从主题字符串中挑选出的字符串开头,后跟与正则表达式中每个子表达式按出现顺序匹配的部分。

  • {parts,N} - 指定要将主题字符串拆分为的部分数。

    部分数对于特定的最大部分数应为正整数,对于最大可能部分数应为 infinity(默认值)。指定 {parts,0} 将会给出尽可能多的部分,忽略末尾的空部分,与指定 trim 相同。

  • trim - 指定结果列表末尾的空部分将被忽略。与指定 {parts,0} 相同。这对应于 Perl 中 split 内置函数的默认行为。

链接到此函数

version()

查看源代码 (自 OTP 20.0 起)
-spec version() -> binary().

此函数的返回结果是一个字符串,其中包含 Erlang/OTP 编译中使用的系统的 PCRE 版本。