perlre - Perl 正则表达式
此页面描述了 Perl 中正则表达式的语法。
如果您以前没有使用过正则表达式,可以在 perlretut 中找到教程介绍。如果您只了解一点点,可以在 perlrequick 中找到快速入门介绍。
除了 "基础" 部分,本页面假定您熟悉正则表达式基础,例如什么是“模式”,它是什么样子以及如何基本使用它。有关如何使用它们以及各种示例的参考,请参阅 "perlop 中的正则表达式引用类运算符" 中对 m//
、s///
、qr//
和 "??"
的讨论。
v5.22 中的新增功能 use re 'strict'
在编译正则表达式模式时应用比其他情况更严格的规则。它可以找到一些虽然合法,但可能不是您想要的规则。
正则表达式是具有非常特殊语法和含义的字符串,如本文档和本文档引用的辅助文档中所述。这些字符串称为“模式”。模式用于确定另一个称为“目标”的字符串是否具有(或不具有)模式指定的特征。我们称之为根据模式“匹配”目标字符串。通常,匹配是通过将目标作为第一个操作数,将模式作为第二个操作数,在 perlop 中的“绑定运算符” 中的两个二元运算符 =~
和 !~
中的一个进行的;并且模式将通过 perlop 中的“Regexp 引用类运算符” 中的运算符之一从普通字符串转换,如下所示
$foo =~ m/abc/
当且仅当变量 $foo
中的字符串包含字符序列“a”、“b”,然后是“c”时,此表达式才求值为真。(=~ m
或匹配运算符在 perlop 中的“m/PATTERN/msixpodualngc” 中进行了说明。)
尚未存储在某个变量中的模式必须在两端用分隔符字符分隔。这些通常像上面的示例中一样是正斜杠,并且在文档中编写模式的典型方式是使用这些斜杠。在大多数情况下,分隔符是前后相同的字符,但在少数情况下,一个字符看起来像它的镜像配偶,其中打开版本是开始分隔符,而关闭版本是结束分隔符,如下所示
$foo =~ m<abc>
大多数情况下,模式在双引号上下文中求值,但可以选择分隔符以强制使用单引号,如下所示
$foo =~ m'abc'
如果模式包含其分隔符,则必须转义该分隔符。用反斜杠为其添加前缀(例如,"/foo\/bar/"
)即可实现此目的。
模式中的任何单个字符都与目标字符串中的相同字符匹配,除非该字符是本文档中描述的具有特殊含义的元字符。非元字符序列与目标字符串中的相同序列匹配,如我们在上面的 m/abc/
中所看到的。
只有少数几个字符(全部是 ASCII 标点字符)是元字符。最常用的一个是一个点 "."
,它通常匹配几乎任何字符(包括点本身)。
您可以通过给通常用作元字符的字符添加前缀 "\"
来使其按字面意思解释,就像如果模式的分隔符也出现在模式中,则必须转义它一样。因此,"\."
仅匹配一个字面上的点 "."
,而不是其通常的含义。这意味着反斜杠也是一个元字符,因此 "\\"
匹配一个 "\"
。并且包含转义元字符的序列与目标字符串中的相同序列(但不带转义)匹配。因此,模式 /blur\\fl/
将匹配包含序列 "blur\fl"
的任何目标字符串。
元字符 "|"
用于匹配一件事或另一件事。因此
$foo =~ m/this|that/
当且仅当$foo
包含序列"this"
或序列"that"
时为 TRUE。与所有元字符一样,用反斜杠作为"|"
前缀使其匹配普通标点字符;在本例中,为竖线。
$foo =~ m/this\|that/
当且仅当$foo
包含序列"this|that"
时为 TRUE。
您并不仅限于单个"|"
。
$foo =~ m/fee|fie|foe|fum/
当且仅当$foo
包含儿童故事“杰克与魔豆”中的 4 个序列中的任何一个时为 TRUE。
正如您所见,"|"
的结合力弱于普通字符序列。我们可以使用分组元字符(圆括号"("
和")"
)来覆盖此项。
$foo =~ m/th(is|at) thing/
当且仅当$foo
包含序列"this thing"
或序列"that thing"
时为 TRUE。与括号中模式部分匹配的字符串部分通常可单独使用,以便稍后在模式、替换或程序中使用。这称为“捕获”,它可能会很复杂。请参阅"捕获组"。
第一个备选项包括从最后一个模式分隔符("("
、"(?:"
(稍后描述)、等或模式开头)到第一个"|"
的所有内容,最后一个备选项包含从最后一个"|"
到下一个关闭模式分隔符的所有内容。这就是为什么将备选项包含在括号中是常见做法:最大程度地减少对它们从何处开始和结束的困惑。
备选项从左到右尝试,因此找到的第一个备选项(整个表达式与之匹配),就是所选备选项。这意味着备选项不一定贪婪。例如:当将foo|foot
与"barefoot"
匹配时,只有"foo"
部分将匹配,因为这是尝试的第一个备选项,并且它成功匹配目标字符串。(这看起来可能并不重要,但当您使用圆括号捕获匹配文本时,它很重要。)
除了消除元字符的特殊含义,前缀反斜杠还会改变一些字母和数字字符,使其不再匹配自身,而是具有特殊含义。这些称为“转义序列”,所有这些都将在 perlrebackslash 中进行描述。如果启用了警告,则 Perl 当前没有特殊含义的反斜杠序列(字母或数字)将引发警告,因为这些序列保留供将来使用。
一个这样的序列是 \b
,它匹配某种边界。\b{wb}
和其他一些序列提供了专门类型的边界。(它们都将在 perlrebackslash 中的“\b{}, \b, \B{}, \B” 中进行详细描述。)请注意,这些不匹配字符,而是字符之间的零宽空格。它们是 零宽断言 的一个示例。再次考虑,
$foo =~ m/fee|fie|foe|fum/
如果除了这 4 个单词之外,序列“feed”、“field”、“Defoe”、“fume”和许多其他序列都在 $foo
中,则它将计算为 TRUE。通过明智地使用 \b
(或更好的(因为它旨在处理自然语言)\b{wb}
),我们可以确保只匹配巨人的单词
$foo =~ m/\b(fee|fie|foe|fum)\b/
$foo =~ m/\b{wb}(fee|fie|foe|fum)\b{wb}/
最后一个示例显示字符 "{"
和 "}"
是元字符。
转义序列的另一个用途是指定不能(或您不想)按字面意思书写的字符。这些在 perlrebackslash 中的“字符转义” 中进行了详细描述,但接下来的三段简要描述了其中一些。
可以使用 C 语言样式编写各种控制字符:"\n"
匹配换行符,"\t"
匹配制表符,"\r"
匹配回车符,"\f"
匹配换页符,等等。
更一般地,\nnn
(其中 nnn 是一个由三个八进制数字组成的字符串)匹配其本机代码点为 nnn 的字符。如果您没有恰好三个数字,则很容易遇到麻烦。因此,请始终使用三个,或者自 Perl 5.14 起,您可以使用 \o{...}
指定任意数量的八进制数字。
类似地,\xnn
(其中 nn 是十六进制数字)匹配其本机序数为 nn 的字符。同样,不使用恰好两个数字是造成灾难的秘诀,但您可以使用 \x{...}
指定任意数量的十六进制数字。
除了是元字符之外,"."
还是“字符类”的一个示例,它可以匹配给定字符集中任何单个字符。就其而言,该集合几乎是所有可能的字符。除了 "."
之外,Perl 还预定义了几个字符类;有一个专门的参考页面,即 perlrecharclass。
您可以通过在模式中将您想要在集合中的所有字符列表放在适当的位置来定义您自己的自定义字符类。您可以通过将列表括在 []
括号字符中来执行此操作。当我们精确时,这些称为“带括号的字符类”,但通常会省略“带括号”一词。(省略它通常不会造成混淆。)这意味着 "["
字符是另一个元字符。它本身不匹配任何内容;它仅用于告诉 Perl 它后面的是一个带括号的字符类。如果您想匹配一个左方括号,您必须对其进行转义,例如 "\["
。匹配的 "]"
也是一个元字符;同样,它本身不匹配任何内容,但只是向 Perl 标记您自定义类的结尾。它是一个“有时元字符”的示例。如果没有相应的 "["
,它就不是元字符,并匹配其字面意思
print "]" =~ /]/; # prints 1
字符类中的字符列表给出了类匹配的字符集。"[abc]"
匹配单个 "a" 或 "b" 或 "c"。但如果 "["
后的第一个字符是 "^"
,则类匹配列表中没有的任何字符。在列表中,"-"
字符指定一个字符范围,因此 a-z
表示介于 "a" 和 "z"(包括两者)之间的所有字符。如果您希望 "-"
或 "]"
本身成为类的成员,请将其放在列表的开头(可能在 "^"
之后),或使用反斜杠对其进行转义。当 "-"
位于列表的末尾,紧靠在结束 "]"
之前时,它也会被当作文本字符。 (以下所有都指定了相同的三字符类:[-az]
、[az-]
和 [a\-z]
。所有这些都不同于 [a-z]
,后者指定一个包含二十六个字符的类,即使在基于 EBCDIC 的字符集中也是如此。)
方括号字符类还有更多内容;完整详细信息请参阅 perlrecharclass 中的“方括号字符类”。
“基础知识” 介绍了一些元字符。本节将给出所有元字符。它们中的大多数具有与 egrep 命令中相同的含义。
只有 "\"
始终是元字符。其他字符有时是元字符。下表列出了所有元字符,总结了它们的用法,并给出了它们作为元字符的上下文。在这些上下文之外或在 "\"
前缀的情况下,它们匹配其对应的标点字符。在某些情况下,它们的含义会根据更改默认行为的各种模式修饰符而有所不同。请参阅 “修饰符”。
PURPOSE WHERE
\ Escape the next character Always, except when
escaped by another \
^ Match the beginning of the string Not in []
(or line, if /m is used)
^ Complement the [] class At the beginning of []
. Match any single character except newline Not in []
(under /s, includes newline)
$ Match the end of the string Not in [], but can
(or before newline at the end of the mean interpolate a
string; or before any newline if /m is scalar
used)
| Alternation Not in []
() Grouping Not in []
[ Start Bracketed Character class Not in []
] End Bracketed Character class Only in [], and
not first
* Matches the preceding element 0 or more Not in []
times
+ Matches the preceding element 1 or more Not in []
times
? Matches the preceding element 0 or 1 Not in []
times
{ Starts a sequence that gives number(s) Not in []
of times the preceding element can be
matched
{ when following certain escape sequences
starts a modifier to the meaning of the
sequence
} End sequence started by {
- Indicates a range Only in [] interior
# Beginning of comment, extends to line end Only with /x modifier
请注意,大多数元字符在出现在方括号字符类中时会失去其特殊含义,但当 "^"
出现在此类类的开头时,它具有不同的含义。而 "-"
和 "]"
仅在方括号字符类中的受限位置处是元字符;而 "}"
仅在关闭由 "{"
开始的特殊结构时才是元字符。
在双引号上下文中(通常情况下),您需要注意 "$"
和非元字符 "@"
。这些可以内插变量,这可能不是您想要的。
这些规则旨在紧凑地表达,而不是可读性和可维护性。"/x 和 /xx" 模式修饰符允许你插入空格以提高可读性。使用 re 'strict'
会增加额外的检查,以捕获一些可能在不知不觉中编译成非预期内容的错别字。
默认情况下,"^"
字符保证仅匹配字符串的开头,"$"
字符仅匹配结尾(或结尾前的换行符),并且 Perl 会对字符串仅包含一行这一假设进行某些优化。嵌入的换行符不会被 "^"
或 "$"
匹配。但是,你可能希望将字符串视为多行缓冲区,以便 "^"
将匹配字符串中任何换行符之后的内容(除非换行符是字符串中的最后一个字符),并且 "$"
将匹配任何换行符之前的内容。通过在模式匹配运算符上使用 "/m"
修饰符,你可以以付出一点额外开销为代价来实现这一点。(旧程序通过设置 $*
来实现这一点,但此选项已在 perl 5.10 中删除。)
为了简化多行替换,"."
字符永远不会匹配换行符,除非你使用 /s
修饰符,它实际上告诉 Perl 假装字符串是一行——即使它不是。
可以使用各种修饰符来更改匹配的默认行为。与模式解释相关的修饰符列在正下方。有关 Perl 使用模式的方式的修饰符的详细信息,请参阅 perlop 中的“Regexp 引用式运算符” 和 perlop 中的“解析引用式结构的可怕细节”。可以动态添加修饰符;请参阅下面的 “扩展模式”。
m
将要匹配的字符串视为多行。也就是说,将 "^"
和 "$"
从匹配字符串第一行的开头和最后一行结尾更改为匹配字符串中每行的开头和结尾。
s
将字符串视为单行。也就是说,将 "."
更改为匹配任何字符,即使是换行符,而通常情况下它不会匹配换行符。
与 /ms
一起使用时,它们允许 "."
匹配任何字符,同时仍然允许 "^"
和 "$"
分别匹配字符串中换行符之后的和换行符之前的字符。
i
执行不区分大小写的模式匹配。例如,“A”将在/i
下匹配“a”。
如果区域匹配规则有效,则对于小于 255 的代码点,字符映射将从当前区域获取,对于较大的代码点,将从 Unicode 规则获取。但是,除非区域是 UTF-8 区域,否则跨越 Unicode 规则/非 Unicode 规则边界(序数 255/256)的匹配将不会成功。请参见 perllocale。
有很多 Unicode 字符在/i
下匹配多个字符的序列。例如,LATIN SMALL LIGATURE FI
应该匹配序列fi
。当多个字符在模式中并且在分组之间被分割,或者当一个或多个被量化时,Perl 目前无法做到这一点。因此
"\N{LATIN SMALL LIGATURE FI}" =~ /fi/i; # Matches
"\N{LATIN SMALL LIGATURE FI}" =~ /[fi][fi]/i; # Doesn't match!
"\N{LATIN SMALL LIGATURE FI}" =~ /fi*/i; # Doesn't match!
# The below doesn't match, and it isn't clear what $1 and $2 would
# be even if it did!!
"\N{LATIN SMALL LIGATURE FI}" =~ /(f)(i)/i; # Doesn't match!
除非明确提及映射到它们的字符,否则 Perl 不会匹配方括号字符类中的多个字符,并且如果字符类被反转,它根本不会匹配它们,否则可能会造成极大的混淆。请参见 perlrecharclass 中的“方括号字符类” 和 perlrecharclass 中的“否定”。
x
和xx
允许空白和注释,以扩展模式的可读性。详情请参见 “/x 和 /xx”
p
保留匹配的字符串,以便在匹配后可以使用${^PREMATCH}
、${^MATCH}
和${^POSTMATCH}
。
在 Perl 5.20 及更高版本中,这将被忽略。由于采用了新的写时复制机制,因此无论使用何种修饰符,在匹配后都可以使用${^PREMATCH}
、${^MATCH}
和${^POSTMATCH}
。
a
、d
、l
和u
这些修饰符(全部在 5.14 中新增)影响使用哪些字符集规则(Unicode 等),如下文 “字符集修饰符” 中所述。
n
防止分组元字符 ()
捕获。此修饰符在 5.22 中新增,将阻止 $1
、$2
等填充。
"hello" =~ /(hi|hello)/; # $1 is "hello"
"hello" =~ /(hi|hello)/n; # $1 is undef
这等同于在每个捕获组的开头放置 ?:
"hello" =~ /(?:hi|hello)/; # $1 is undef
/n
可以逐组否定。或者,仍然可以使用命名捕获。
"hello" =~ /(?-n:(hi|hello))/n; # $1 is "hello"
"hello" =~ /(?<greet>hi|hello)/n; # $1 is "hello", $+{greet} is
# "hello"
有一些标志可以在正则表达式结构的末尾找到,它们不是通用正则表达式标志,而是应用于正在执行的操作,例如匹配或替换(分别为 m//
或 s///
)。
在 perlretut 中的“在 Perl 中使用正则表达式”中进一步描述的标志是
c - keep the current position during repeated matching
g - globally match the pattern repeatedly in the string
在 perlop 中的“s/PATTERN/REPLACEMENT/msixpodualngcer”中描述的特定于替换的修饰符是
e - evaluate the right-hand side as an expression
ee - evaluate the right side as a string then eval the result
o - pretend to optimize your code, but actually introduce bugs
r - perform non-destructive substitution and return the new value
正则表达式修饰符通常在文档中写为例如,“/x
修饰符”,即使有问题的分隔符实际上可能不是斜杠。修饰符 /imnsxadlup
也可以使用 (?...)
结构嵌入到正则表达式本身中,请参见下面的“扩展模式”。
一些修饰符需要比上面“概述”中给出的更多解释。
/x
和 /xx
单个 /x
告诉正则表达式解析器忽略大多数既不是反斜杠也不是在方括号字符类中或在多字符元模式的字符中(如 (?i: ... )
)的空格。您可以使用它将正则表达式分解为更具可读性的部分。此外,"#"
字符被视为一个元字符,它引入一个注释,该注释一直运行到模式的结束分隔符,或者如果模式扩展到下一行,则运行到当前行的末尾。因此,这非常类似于普通的 Perl 代码注释。(只有在您在注释之前加上反斜杠时,您才能在注释中包含结束分隔符,所以要小心!)
使用 /x
意味着如果你希望在模式中出现真正的空白或 "#"
字符(在方括号字符类之外,不受 /x
影响),那么你必须转义它们(使用反斜杠或 \Q...\E
)或使用八进制、十六进制或 \N{}
或 \p{name=...}
转义对它们进行编码。尝试通过使用反斜杠或 \Q
转义 \n
来将注释延续到下一行是无效的。
你可以使用 "(?#text)" 来创建一个在当前行结束之前就结束的注释,但 text
也不能包含结束分隔符,除非使用反斜杠对其进行转义。
一个常见的陷阱是忘记 "#"
字符(在方括号字符类之外)在 /x
下开始注释,并且不能按字面意思匹配。在尝试找出为什么特定的 /x
模式没有按预期工作时,请记住这一点。在方括号字符类中,"#"
保留其非特殊字面意思。
从 Perl v5.26 开始,如果修饰符在其内部有第二个 "x"
,则单个 /x
的效果会增加。唯一的区别是,在方括号字符类中,未转义(通过反斜杠)的 SPACE 和 TAB 字符不会添加到类中,因此可以插入它们以使类更具可读性
/ [d-e g-i 3-7]/xx
/[ ! @ " # $ % ^ & * () = ? <> ' ]/xx
可能比压缩的等价物更容易理解
/[d-eg-i3-7]/
/[!@"#$%^&*()=?<>']/
请注意,这不幸地并不意味着你的方括号类可以包含注释或扩展到多行。字符类中的 #
仍然只是一个字面 #
,并且不会引入注释。而且,除非结束括号与开始括号在同一行上,否则换行符(以及下一行(s) 中直到 ]
终止的所有内容)将成为该类的组成部分,就像你编写 \n
一样。
综合考虑,这些特性在很大程度上提高了 Perl 正则表达式的可读性。以下是一个示例
# Delete (most) C comments.
$program =~ s {
/\* # Match the opening delimiter.
.*? # Match a minimal number of characters.
\*/ # Match the closing delimiter.
} []gsx;
请注意,\Q...\E
中的任何内容都不会受到 /x
的影响。并且请注意,/x
不会影响单个多字符构造中的空格解释。例如,(?:...)
不能在 "("
、"?"
和 ":"
之间有空格。在此类构造的任何分隔符内,允许的空格不受 /x
影响,并且取决于构造。例如,所有使用花括号作为分隔符的构造,例如 \x{...}
可以在花括号内但紧邻花括号处有空格,但不能在其他地方有空格,并且没有非空格字符。一个例外是遵循 Unicode 规则的 Unicode 属性,有关这些属性,请参阅 "Properties accessible through \p{} and \P{}" in perluniprops。
被视为空白的字符集是 Unicode 称为“模式空白”的字符集,即
U+0009 CHARACTER TABULATION
U+000A LINE FEED
U+000B LINE TABULATION
U+000C FORM FEED
U+000D CARRIAGE RETURN
U+0020 SPACE
U+0085 NEXT LINE
U+200E LEFT-TO-RIGHT MARK
U+200F RIGHT-TO-LEFT MARK
U+2028 LINE SEPARATOR
U+2029 PARAGRAPH SEPARATOR
/d
、/u
、/a
和 /l
(从 5.14 开始可用)称为字符集修饰符;它们影响用于正则表达式的字符集规则。
/d
、/u
和 /l
修饰符对你来说可能没什么用,所以你不需要太担心它们。它们存在于 Perl 的内部使用中,以便可以自动序列化复杂的正则表达式数据结构,并在以后精确地重建它们,包括它们的全部细微差别。但是,由于 Perl 无法保守秘密,并且在极少数情况下它们可能有用,因此在此记录它们。
另一方面,/a
修饰符可能很有用。它的目的是允许主要处理 ASCII 数据的代码不必关心 Unicode。
简而言之,/l
将字符集设置为在模式匹配执行时生效的任何 Locale 的字符集。
/u
将字符集设置为 Unicode。
/a
也将字符集设置为 Unicode,但添加了几个针对 ASCII 安全匹配的限制。
/d
是旧的、有问题的、5.14 之前的 Default 字符集行为。它的唯一用途是强制执行旧行为。
在任何给定时间,这些修饰符中只有一个有效。它们的出现允许 Perl 保持正则表达式的原始编译行为,无论在实际执行时生效的是什么规则。如果它内插到一个更大的正则表达式中,则原始规则继续适用于它,并且不影响其他部分。
/l
和 /u
修饰符会自动选择在各种编译范围内的正则表达式中,我们建议通常使用这些编译范围,而不是显式指定这些修饰符。一方面,修饰符只影响模式匹配,甚至不扩展到任何替换,而使用编译范围则在其范围内的所有适当操作中提供一致的结果。例如,
s/foo/\Ubar/il
将使用区域设置的规则匹配“foo”,以进行不区分大小写的匹配,但 /l
不影响 \U
的操作方式。您很可能希望它们都使用区域设置规则。为此,请在 use locale
的范围内编译正则表达式。这既隐式添加了 /l
,又将区域设置规则应用于 \U
。教训是使用 use locale
,而不是显式使用 /l
。
类似地,最好使用 use feature 'unicode_strings'
而不是,
s/foo/\Lbar/iu
获取 Unicode 规则,因为前者(但不一定是后者)中的 \L
也将使用 Unicode 规则。
下面将详细介绍每个修饰符。您很可能不需要了解 /l
、/u
和 /d
的详细信息,可以跳到 /a。
表示在模式匹配时使用当前区域设置的规则(请参阅 perllocale)。例如,\w
将匹配该区域设置的“单词”字符,而 "/i"
不区分大小写的匹配将根据区域设置的折叠规则进行匹配。使用的区域设置将在模式匹配执行时生效。这可能与编译时区域设置不同,并且如果在中间调用 setlocale() 函数,则可能与一次匹配到另一次匹配有所不同。
在 v5.20 之前,Perl 不支持多字节区域设置。从那时起,开始支持 UTF-8 区域设置。以后也不太可能支持其他多字节区域设置。但是,在所有区域设置中,都可以有超过 255 的代码点,无论当前区域设置是什么,这些代码点都将始终被视为 Unicode。
根据 Unicode 规则,有一些不区分大小写的匹配会跨越 255/256 边界。除了 Perls v5.20 及更高版本中的 UTF-8 区域设置外,这些在 /l
下是不允许的。例如,0xFF(在 ASCII 平台上)不会不区分大小写地匹配 0x178 处的字符,即 LATIN CAPITAL LETTER Y WITH DIAERESIS
,因为 0xFF 在当前区域设置中可能不是 LATIN SMALL LETTER Y WITH DIAERESIS
,而 Perl 无法知道该字符是否甚至存在于区域设置中,更不用说它是什么代码点了。
在 v5.20 及更高版本的 UTF-8 区域设置中,正则表达式中区域设置与非区域设置之间唯一可见的差异应该是污染,如果您的 perl 支持污染检查(请参阅 perlsec)。
可以使用 use locale
将此修饰符指定为默认值,但请参阅 "哪个字符集修饰符有效?"。
表示在模式匹配时使用 Unicode 规则。在 ASCII 平台上,这意味着 128 到 255 之间的代码点采用它们的 Latin-1 (ISO-8859-1) 含义(与 Unicode 的含义相同)。(否则,Perl 认为它们的含义未定义。)因此,在此修饰符下,ASCII 平台实际上变成了 Unicode 平台;因此,例如,\w
将匹配 Unicode 中超过 100,000 个单词字符中的任何一个。
与大多数特定于语言和国家/地区对的区域设置不同,Unicode 将世界上某个地方的字母字符都归类为 \w
。例如,您的区域设置可能认为 LATIN SMALL LETTER ETH
不是字母(除非您碰巧会说冰岛语),但 Unicode 认为是。同样,世界上某个地方的所有十进制数字字符都将匹配 \d
;这是数百个可能的匹配,而不是 10 个。其中一些数字看起来像 10 个 ASCII 数字,但表示不同的数字,因此人类很容易认为某个数字与实际数字不同。例如,BENGALI DIGIT FOUR
(U+09EA) 看起来非常像 ASCII DIGIT EIGHT
(U+0038),而 LEPCHA DIGIT SIX
(U+1C46) 看起来非常像 ASCII DIGIT FIVE
(U+0035)。并且,\d+
可能会匹配来自不同书写系统的数字字符串,从而造成安全问题。例如,欺诈性网站可以使用 U+1C46 显示某物的价格,并且用户会认为某物花费了 500 个单位,但实际上花费了 600 个单位。强制执行脚本运行的浏览器("Script Runs")将阻止这种欺诈性显示。"num()" in Unicode::UCD 也可以用来解决这个问题。或者可以使用 /a
修饰符强制 \d
仅匹配 ASCII 0 到 9。
此外,在此修饰符下,不区分大小写的匹配适用于全套 Unicode 字符。例如,KELVIN SIGN
匹配字母“k”和“K”;而 LATIN SMALL LIGATURE FF
匹配序列“ff”,如果您没有准备,它可能看起来像一个十六进制常量,从而造成另一个潜在的安全问题。有关 Unicode 安全问题的详细讨论,请参见 https://unicode.org/reports/tr36。
可以通过 use feature 'unicode_strings
、use locale ':not_characters'
或 use v5.12
(或更高版本)指定此修饰符为默认值,但请参见 "Which character set modifier is in effect?"。
重要提示:由于此修饰符会导致不可预测的行为,因此仅使用它来维护奇怪的向后兼容性。在新代码中使用 unicode_strings
特性以避免无意中默认启用此修饰符。
此修饰符有什么作用?它“取决于”!
此修饰符意味着使用平台原生匹配规则,但当有理由使用 Unicode 规则时除外,如下所示
目标字符串的 UTF8 标志(见下文)已设置;或
模式的 UTF8 标志(见下文)已设置;或
该模式明确提到了一个高于 255 的代码点(例如通过 \x{100}
);或
该模式使用了 Unicode 名称(\N{...}
);或
该模式使用了 Unicode 属性(\p{...}
或 \P{...}
);或
该模式使用了 Unicode 断行(\b{...}
或 \B{...}
);或
该模式使用了 "(?[ ])"
该模式使用了 (*script_run: ...)
关于上述“UTF8 标志”的引用:通常 Perl 应用程序不应该考虑该标志。它是 Perl 内部的一部分,因此它可以在 Perl 需要时发生更改。因此,/d
可能会导致不可预测的结果。请参阅 perlunicode 中的 "perlunicode 中的“Unicode Bug”。此错误已变得相当臭名昭著,导致此修饰符出现了其他(不带脏话)名称,例如“Dicey”和“Dodgy”。
以下是一些有关如何在 ASCII 平台上使用它的示例
$str = "\xDF"; #
utf8::downgrade($str); # $str is not UTF8-flagged.
$str =~ /^\w/; # No match, since no UTF8 flag.
$str .= "\x{0e0b}"; # Now $str is UTF8-flagged.
$str =~ /^\w/; # Match! $str is now UTF8-flagged.
chop $str;
$str =~ /^\w/; # Still a match! $str retains its UTF8 flag.
在 Perl 的默认配置下,当没有选择其他配置时,此修饰符会自动默认选择,因此它还有另一个名称(不幸的是)为“Default”。
只要有可能,请使用 unicode_strings
来使其成为默认值。
此修饰符代表 ASCII 限制(或 ASCII 安全)。此修饰符可以加倍以增强其效果。
当它单独出现时,它会导致序列 \d
、\s
、\w
和 Posix 字符类仅在 ASCII 范围内匹配。因此,它们恢复到 5.6 之前的、Unicode 之前的含义。在 /a
下,\d
始终精确地表示数字 "0"
到 "9"
;\s
表示五个字符 [ \f\n\r\t]
,从 Perl v5.18 开始,表示垂直制表符;\w
表示 63 个字符 [A-Za-z0-9_]
;同样,所有 Posix 类(例如 [[:print:]]
)仅匹配适当的 ASCII 范围字符。
此修饰符对于仅偶然使用 Unicode 且不希望因其复杂性和安全问题而负担的人很有用。
使用 /a
,可以自信地使用 \d
来匹配 ASCII 字符,如果需要匹配 ASCII 之外的字符,可以使用 \p{Digit}
(或 \p{Word}
替换 \w
)。有类似的 \p{...}
结构,可以匹配 ASCII 之外的空白(参见 "Whitespace" in perlrecharclass)和 Posix 类(参见 "POSIX Character Classes" in perlrecharclass)。因此,此修饰符并不意味着不能使用 Unicode,而是意味着要获得 Unicode 匹配,必须显式使用表示 Unicode 的结构(\p{}
、\P{}
)。
正如你所期望的,此修饰符会导致 \D
与 [^0-9]
的含义相同;事实上,所有非 ASCII 字符都匹配 \D
、\S
和 \W
。\b
仍然表示匹配 \w
和 \W
之间的边界,使用它们的 /a
定义(\B
类似)。
否则,/a
的行为类似于 /u
修饰符,因为不区分大小写的匹配使用 Unicode 规则;例如,“k” 将在 /i
匹配下匹配 Unicode \N{KELVIN SIGN}
,并且在不区分大小写匹配时,拉丁语 1 范围内的代码点高于 ASCII 将具有 Unicode 规则。
要禁止 ASCII/非 ASCII 匹配(例如使用 \N{KELVIN SIGN}
的“k”),请指定 "a"
两次,例如 /aai
或 /aia
。("a"
的第一次出现限制了 \d
等,第二次出现添加了 /i
限制。)但是,请注意,ASCII 范围之外的代码点将对 /i
匹配使用 Unicode 规则,因此该修饰符实际上并没有将内容限制为仅 ASCII;它只是禁止了 ASCII 和非 ASCII 的混合。
总之,此修饰符为不希望接触所有 Unicode 的应用程序提供了保护。指定两次可提供额外的保护。
可以通过 use re '/a'
或 use re '/aa'
指定此修饰符为默认值。如果你这样做,实际上你可能有机会显式使用 /u
修饰符,如果有一些正则表达式确实需要完整的 Unicode 规则(但即使在这里,最好是所有内容都在特性 "unicode_strings"
下,以及 use re '/aa'
)。另请参见 "Which character set modifier is in effect?"。
在正则表达式的任何给定点上,哪个修饰符有效取决于一组相当复杂的交互。这些设计使得通常不必担心它,但本节提供了血腥的细节。如下文 "Extended Patterns" 中所述,可以显式指定仅适用于正则表达式部分的修饰符。最内部的始终优先于任何外部的,而适用于整个表达式的优先于本节其余部分中描述的任何默认设置。
use re '/foo'
pragma 可用于为其作用域内编译的正则表达式设置默认修饰符(包括这些)。此 pragma 优先于下面列出的其他 pragma,这些 pragma 也会更改默认值。
否则,use locale
将默认修饰符设置为 /l
;而 use feature 'unicode_strings
,或 use v5.12
(或更高版本)将默认值设置为 /u
,当不在 use locale
或 use bytes
的作用域内时。(use locale ':not_characters'
还将默认值设置为 /u
,覆盖任何普通的 use locale
。)与上面提到的机制不同,这些机制会影响正则表达式模式匹配之外的操作,因此与其他运算符(包括在替换中使用 \U
、\l
、等)一起使用时,会产生更一致的结果。
如果以上都不适用,出于向后兼容性的原因,/d
修饰符是默认生效的。由于这可能导致意外结果,因此最好指定应使用哪种其他规则集。
在 5.14 之前,没有显式修饰符,但对于在 use locale
的作用域内编译的正则表达式,隐含了 /l
,否则隐含了 /d
。但是,将正则表达式插值到更大的正则表达式中将忽略原始编译,而支持在第二次编译时生效的任何内容。/d
修饰符存在许多不一致(错误),在不适当的情况下会使用 Unicode 规则,反之亦然。\p{}
并不意味着 Unicode 规则,直到 5.12,\N{}
的所有出现也不意味着 Unicode 规则。
当模式的特定部分需要匹配一定次数(或次数)时,使用量词。如果没有量词,则匹配的次数恰好为一次。识别以下标准量词
* Match 0 or more times
+ Match 1 or more times
? Match 1 or 0 times
{n} Match exactly n times
{n,} Match at least n times
{,n} Match at most n times
{n,m} Match at least n but not more than m times
(如果非转义的大括号出现在上述量词之外的上下文中,并且它不构成反斜杠序列的一部分,如 \x{...}
,则它要么是致命语法错误,要么被视为常规字符,通常会引发弃用警告。要转义它,可以在其前面加上反斜杠("\{"
)或将其括在方括号中("[{]"
)。此更改将允许将来的语法扩展(如使量词的下限可选),并更好地检查量词的错误)。
"*"
量词等效于 {0,}
,"+"
量词等效于 {1,}
,"?"
量词等效于 {0,1}
。n 和 m 仅限于在 perl 构建时定义的预设限制以下的非负整数。在最常见的平台上,这通常为 65534。实际限制可以在此类代码生成的错误消息中看到
$_ **= $_ , / {$_} / for 2 .. 42;
默认情况下,量化的子模式是“贪婪的”,也就是说,它将尽可能多地匹配(给定一个特定的起始位置),同时仍然允许模式的其余部分匹配。如果你希望它匹配尽可能少的次数,请在量词后面加上 "?"
。请注意,含义不会改变,只是“贪婪”程度改变了
*? Match 0 or more times, not greedily
+? Match 1 or more times, not greedily
?? Match 0 or 1 time, not greedily
{n}? Match exactly n times, not greedily (redundant)
{n,}? Match at least n times, not greedily
{,n}? Match at most n times, not greedily
{n,m}? Match at least n but not more than m times, not greedily
通常,当量化的子模式不允许模式的其余部分匹配时,Perl 将回溯。但是,有时这种行为是不希望的。因此,Perl 还提供了“独占”量词形式。
*+ Match 0 or more times and give nothing back
++ Match 1 or more times and give nothing back
?+ Match 0 or 1 time and give nothing back
{n}+ Match exactly n times and give nothing back (redundant)
{n,}+ Match at least n times and give nothing back
{,n}+ Match at most n times and give nothing back
{n,m}+ Match at least n but not more than m times and give nothing back
例如,
'aaaa' =~ /a++a/
将永远不会匹配,因为 a++
将吞噬字符串中的所有 "a"
,而不会为模式的其余部分留下任何 "a"
。此功能对于向 perl 提供有关它不应回溯的位置的提示非常有用。例如,当写成
/"(?:[^"\\]++|\\.)*+"/
时,可以最有效地执行典型的“匹配双引号字符串”问题,因为我们知道,如果最终引号不匹配,回溯将无济于事。有关更多详细信息,请参阅独立子表达式 "(?>pattern)"
;独占量词只是该结构的语法糖。例如,上面的示例也可以写成如下形式
/"(?>(?:(?>[^"\\]+)|\\.)*)"/
请注意,独占量词修饰符不能与非贪婪修饰符结合使用。这是因为它没有意义。考虑以下等效表
Illegal Legal
------------ ------
X??+ X{0}
X+?+ X{1}
X{min,max}?+ X{min}
由于模式被视为双引号字符串进行处理,因此以下内容也可以使用
\t tab (HT, TAB)
\n newline (LF, NL)
\r return (CR)
\f form feed (FF)
\a alarm (bell) (BEL)
\e escape (think troff) (ESC)
\cK control char (example: VT)
\x{}, \x00 character whose ordinal is the given hexadecimal number
\N{name} named Unicode character or character sequence
\N{U+263D} Unicode character (example: FIRST QUARTER MOON)
\o{}, \000 character whose ordinal is the given octal number
\l lowercase next char (think vi)
\u uppercase next char (think vi)
\L lowercase until \E (think vi)
\U uppercase until \E (think vi)
\Q quote (disable) pattern metacharacters until \E
\E end either case modification or quoted section, think vi
详细信息请参阅 perlop 中的“引号和类似引号的运算符”。
此外,Perl 定义了以下内容
Sequence Note Description
[...] [1] Match a character according to the rules of the
bracketed character class defined by the "...".
Example: [a-z] matches "a" or "b" or "c" ... or "z"
[[:...:]] [2] Match a character according to the rules of the POSIX
character class "..." within the outer bracketed
character class. Example: [[:upper:]] matches any
uppercase character.
(?[...]) [8] Extended bracketed character class
\w [3] Match a "word" character (alphanumeric plus "_", plus
other connector punctuation chars plus Unicode
marks)
\W [3] Match a non-"word" character
\s [3] Match a whitespace character
\S [3] Match a non-whitespace character
\d [3] Match a decimal digit character
\D [3] Match a non-digit character
\pP [3] Match P, named property. Use \p{Prop} for longer names
\PP [3] Match non-P
\X [4] Match Unicode "eXtended grapheme cluster"
\1 [5] Backreference to a specific capture group or buffer.
'1' may actually be any positive integer.
\g1 [5] Backreference to a specific or previous group,
\g{-1} [5] The number may be negative indicating a relative
previous group and may optionally be wrapped in
curly brackets for safer parsing.
\g{name} [5] Named backreference
\k<name> [5] Named backreference
\k'name' [5] Named backreference
\k{name} [5] Named backreference
\K [6] Keep the stuff left of the \K, don't include it in $&
\N [7] Any character but \n. Not affected by /s modifier
\v [3] Vertical whitespace
\V [3] Not vertical whitespace
\h [3] Horizontal whitespace
\H [3] Not horizontal whitespace
\R [4] Linebreak
有关详细信息,请参阅 perlrecharclass 中的“方括号字符类”。
有关详细信息,请参阅 perlrecharclass 中的“POSIX 字符类”。
有关详细信息,请参阅 perlunicode 中的“Unicode 字符属性”。
有关详细信息,请参阅 perlrebackslash 中的“Misc”。
有关详细信息,请参阅下面的 “捕获组”。
有关详细信息,请参阅下面的 “扩展模式”。
请注意,\N
有两种含义。当为 \N{NAME}
形式时,它匹配名称为 NAME 的字符或字符序列;类似地,当为 \N{U+hex}
形式时,它匹配 Unicode 代码点为 hex 的字符。否则,它匹配除 \n
之外的任何字符。
有关详细信息,请参阅 perlrecharclass 中的“扩展方括号字符类”。
除了 "^"
和 "$"
之外,Perl 还定义了以下零宽断言
\b{} Match at Unicode boundary of specified type
\B{} Match where corresponding \b{} doesn't match
\b Match a \w\W or \W\w boundary
\B Match except at a \w\W or \W\w boundary
\A Match only at beginning of string
\Z Match only at end of string, or before newline at the end
\z Match only at end of string
\G Match only at pos() (e.g. at the end-of-match position
of prior m//g)
Unicode 边界 (\b{}
),从 v5.22 开始可用,是两个字符之间、字符串中第一个字符之前或字符串中最后一个字符之后的一个位置,其中满足 Unicode 定义的某些条件。有关详细信息,请参阅 perlrebackslash 中的“\b{}, \b, \B{}, \B”。
单词边界 (\b
) 是两个字符之间的一个位置,其一侧有 \w
,另一侧有 \W
(按任意顺序),将字符串开头和结尾处的假想字符计为匹配 \W
。(在字符类中,\b
表示退格符,而不是单词边界,就像它在任何双引号字符串中通常所做的那样。)\A
和 \Z
就像 "^"
和 "$"
,不同之处在于,使用 /m
修饰符时,它们不会匹配多次,而 "^"
和 "$"
将在每个内部行边界处匹配。要匹配字符串的实际结尾而不是忽略可选的尾随换行符,请使用 \z
。
\G
断言可用于链接全局匹配(使用 m//g
),如 perlop 中的“Regexp 引用类运算符” 中所述。在编写类似 lex
的扫描程序时,它也很有用,此时您有几个模式要与字符串的连续子字符串进行匹配;请参见前一个参考。\G
将匹配的实际位置也可以通过将 pos()
用作左值来影响:请参见 perlfunc 中的“pos”。请注意,零长度匹配的规则(请参见 “重复模式匹配零长度子字符串”)有所修改,即在确定匹配长度时,不会计算 \G
左侧的内容。因此,以下内容不会永远匹配
my $string = 'ABC';
pos($string) = 1;
while ($string =~ /(.\G)/g) {
print $1;
}
它将打印“A”,然后终止,因为它认为匹配为零宽度,因此不会在同一位置连续匹配两次。
值得注意的是,如果使用不当,\G
可能会导致无限循环。在交替中使用包含 \G
的模式时,请小心。
另请注意,s///
将拒绝覆盖已替换的替换部分;因此,例如,这将在第一次迭代后停止,而不是在字符串中向后迭代
$_ = "123456789";
pos = 6;
s/.(?=.\G)/X/g;
print; # prints 1234X6789, not XXXXX6789
分组构造 ( ... )
创建捕获组(也称为捕获缓冲区)。要在同一模式中稍后引用组的当前内容,请对第一个使用 \g1
(或 \g{1}
),对第二个使用 \g2
(或 \g{2}
),依此类推。这称为反向引用。您可以使用的捕获子字符串数量没有限制。组的编号从最左边的左括号开始,为 1,依此类推。如果组不匹配,则关联的反向引用也不会匹配。(如果组是可选的,或在交替的不同分支中,则可能会发生这种情况。)您可以省略 "g"
,并写 "\1"
,依此类推,但此形式有一些问题,如下所述。
您还可以通过使用负数来相对引用捕获组,以便 \g-1
和 \g{-1}
都引用紧邻的前一个捕获组,而 \g-2
和 \g{-2}
都引用它前面的组。例如
/
(Y) # group 1
( # group 2
(X) # group 3
\g{-1} # backref to group 3
\g{-3} # backref to group 1
)
/x
将与 /(Y) ( (X) \g3 \g1 )/x
匹配相同。这允许您将正则表达式内插到更大的正则表达式中,而不必担心捕获组被重新编号。
你可以完全不用数字,而是创建命名的捕获组。声明的符号是 (?<name>...)
,引用的符号是 \g{name}
。(为了兼容 .Net 正则表达式,\g{name}
也可以写成 \k{name}
、\k<name>
或 \k'name'
。)name 不能以数字开头,也不能包含连字符。当同一模式中的不同组具有相同名称时,对该名称的任何引用都假定为最左边的已定义组。命名组按绝对和相对编号进行计数,因此也可以通过这些编号来引用它们。(可以使用命名捕获组来完成其他情况下需要 (??{})
的操作。)
捕获组内容是动态作用域的,在模式外部可用,直到封闭块结束或在同一作用域中进行下一次成功匹配(以先发生者为准)。有关更多详细信息,请参阅 perlsyn 中的“复合语句” 和 perlvar 中的“正则表达式变量的作用域规则”。
你可以通过绝对编号(使用 "$1"
代替 "\g1"
,等等)或通过 %+
哈希按名称访问捕获组的内容,方法是使用 "$+{name}"
。
在引用命名捕获组时需要大括号,但在引用绝对或相对编号的捕获组时大括号是可选的。在通过连接较小的字符串来创建正则表达式时,使用大括号更安全。例如,如果你有 qr/$a$b/
,并且 $a
包含 "\g1"
,而 $b
包含 "37"
,你将得到 /\g137/
,这可能不是你想要的。
如果你使用大括号,你还可以选择在括号内但紧邻括号处添加任意数量的空格(空格或制表符),例如 \g{ -1 }
或 \k{ name }
。
\g
和 \k
符号是在 Perl 5.10.0 中引入的。在此之前,没有命名或相对编号的捕获组。使用 \1
、\2
、等引用绝对编号的组,并且此符号仍被接受(并且可能一直都会被接受)。但是,如果捕获组超过 9 个,则会导致一些歧义,因为 \10
可能表示第十个捕获组,或八进制序数为 010 的字符(ASCII 中的退格符)。Perl 通过仅在它之前打开了至少 10 个左括号时,将 \10
解释为反向引用来解决此歧义。同样,仅在它之前打开了至少 11 个左括号时,\11
才是一个反向引用。以此类推。\1
到 \9
始终解释为反向引用。下面有几个示例来说明这些危险。如果你指的是捕获组,则可以通过始终使用 \g{}
或 \g
来避免歧义;对于八进制常量,始终使用 \o{}
,或对于 \077
及以下,使用 3 位数并用前导零填充,因为前导零表示八进制常量。
\digit
符号在模式之外的某些情况下也起作用。有关详细信息,请参见下面的 "有关 \1 而不是 $1 的警告"。
示例
s/^([^ ]*) *([^ ]*)/$2 $1/; # swap first two words
/(.)\g1/ # find first doubled char
and print "'$1' is the first doubled character\n";
/(?<char>.)\k<char>/ # ... a different way
and print "'$+{char}' is the first doubled character\n";
/(?'char'.)\g1/ # ... mix and match
and print "'$1' is the first doubled character\n";
if (/Time: (..):(..):(..)/) { # parse out values
$hours = $1;
$minutes = $2;
$seconds = $3;
}
/(.)(.)(.)(.)(.)(.)(.)(.)(.)\g10/ # \g10 is a backreference
/(.)(.)(.)(.)(.)(.)(.)(.)(.)\10/ # \10 is octal
/((.)(.)(.)(.)(.)(.)(.)(.)(.))\10/ # \10 is a backreference
/((.)(.)(.)(.)(.)(.)(.)(.)(.))\010/ # \010 is octal
$a = '(.)\1'; # Creates problems when concatenated.
$b = '(.)\g{1}'; # Avoids the problems.
"aa" =~ /${a}/; # True
"aa" =~ /${b}/; # True
"aa0" =~ /${a}0/; # False!
"aa0" =~ /${b}0/; # True
"aa\x08" =~ /${a}0/; # True!
"aa\x08" =~ /${b}0/; # False
几个特殊变量也引用回前一个匹配的部分。$+
返回最后一个括号匹配匹配的任何内容。$&
返回整个匹配字符串。(有一次 $0
也这样做,但现在它返回程序的名称。)$`
返回匹配字符串之前的所有内容。$'
返回匹配字符串之后的所有内容。$^N
包含最近关闭的组(子匹配)匹配的任何内容。$^N
可用于扩展模式(见下文),例如将子匹配分配给变量。
这些特殊变量(如 %+
哈希和编号匹配变量($1
、$2
、$3
、等))在封闭块结束时或直到下一次成功匹配(以先到者为准)之前动态作用域。(请参见 perlsyn 中的 "复合语句"。)
@{^CAPTURE}
数组可用于访问所有捕获缓冲区,而无需知道有多少个缓冲区。例如
$string=~/$pattern/ and @captured = @{^CAPTURE};
将每个捕获变量的副本($1
、$2
等)放入 @captured
数组中。
请注意,在插入 @{^CAPTURE}
数组的下标时,必须使用分隔的花括号表示法
print "@{^CAPTURE[0]}";
请参阅 perldata 中的“使用花括号分隔变量名”,以了解有关此表示法的更多信息。
注意:Perl 中的匹配失败不会重置匹配变量,这使得编写测试一系列更具体情况并记住最佳匹配的代码变得更容易。
警告:如果你的代码要在 Perl 5.16 或更早版本上运行,请注意,一旦 Perl 发现你可以在程序中的任何位置需要 $&
、$`
或 $'
,它就必须为每个模式匹配提供它们。这可能会大大降低你的程序速度。
Perl 使用相同的机制来生成 $1
、$2
等,因此你还要为包含捕获括号的每个模式付出代价。(要避免此成本,同时保留分组行为,请改用扩展正则表达式 (?: ... )
。)但是,如果你从不使用 $&
、$`
或 $'
,那么不带捕获括号的模式将不会受到惩罚。因此,如果可以,请避免使用 $&
、$'
和 $`
,但如果你不能(并且某些算法确实需要它们),一旦你使用它们一次,就可以随意使用,因为你已经付出了代价。
Perl 5.16 引入了一种稍微更有效的机制,该机制分别记录是否已经看到 $`
、$&
和 $'
,因此可能只需要复制部分字符串。Perl 5.20 引入了一种更有效的写时复制机制,该机制消除了任何速度下降。
作为解决此问题的另一种方法,Perl 5.10.0 引入了 ${^PREMATCH}
、${^MATCH}
和 ${^POSTMATCH}
,它们等效于 $`
、$&
和 $'
,但只有在使用 /p
(保留)修饰符执行成功匹配后才保证它们被定义。与标点字符等效项不同,使用这些变量不会产生全局性能损失,但权衡是你必须告诉 perl 何时要使用它们。从 Perl 5.20 开始,这三个变量等效于 $`
、$&
和 $'
,并且忽略 /p
。
Perl 中的反斜杠元字符是字母数字的,例如 \b
、\w
、\n
。与其他一些正则表达式语言不同,没有非字母数字的反斜杠符号。因此,任何看起来像 \\
、\(
、\)
、\[
、\]
、\{
或 \}
的内容始终被解释为一个文本字符,而不是一个元字符。这曾经用于一种通用惯用语中,用于禁用或引用字符串中正则表达式元字符的特殊含义,而该字符串是要用于模式的。只需引用所有非“单词”字符
$pattern =~ s/(\W)/\\$1/g;
(如果设置了 use locale
,那么这取决于当前区域设置。)现在更常用 quotemeta()
函数或 \Q
元引用转义序列来禁用所有元字符的特殊含义,如下所示
/$unquoted\Q$quoted\E$unquoted/
请注意,如果你在 \Q
和 \E
之间放置文本反斜杠(不在插值变量内),双引号反斜杠插值可能会导致令人困惑的结果。如果你需要在 \Q...\E
中使用文本反斜杠,请参阅 perlop 中的“引号构造的解析细节”。
quotemeta()
和 \Q
在 perlfunc 中的“quotemeta” 中有完整描述。
Perl 还为在 awk 和 lex 等标准工具中找不到的功能定义了一致的扩展语法。其中大多数的语法都是一对括号,括号内的第一个内容是问号。问号后的字符表示扩展。
选择问号作为此项和最小匹配构造,是因为 1) 问号在较旧的正则表达式中很少见,2) 每当你看到问号时,你都应该停下来并“质疑”到底发生了什么。这就是心理学....
(?#text)
一个注释。text 被忽略。请注意,Perl 会在看到 ")"
后立即关闭注释,因此无法在注释中放置文本 ")"
。如果模式的结束定界符出现在注释中,则必须用反斜杠对其进行转义。
请参阅 "/x" 了解在模式中添加注释的另一种方法。
请注意,注释几乎可以放在任何地方,除了转义序列的中间。示例
qr/foo(?#comment)bar/' # Matches 'foobar'
# The pattern below matches 'abcd', 'abccd', or 'abcccd'
qr/abc(?#comment between literal and its quantifier){1,3}d/
# The pattern below generates a syntax error, because the '\p' must
# be followed immediately by a '{'.
qr/\p(?#comment between \p and its property name){Any}/
# The pattern below generates a syntax error, because the initial
# '\(' is a literal opening parenthesis, and so there is nothing
# for the closing ')' to match
qr/\(?#the backslash means this isn't a comment)p{Any}/
# Comments can be used to fold long patterns into multiple lines
qr/First part of a long regex(?#
)remaining part/
(?adlupimnsx-imnsx)
(?^alupimnsx)
零个或多个嵌入式模式匹配修饰符,在模式的剩余部分或封闭模式组的剩余部分(如果存在)中启用(如果前面有 "-"
则禁用)。
这对于动态生成的模式特别有用,例如从配置文件中读取、从参数中获取或在某个表中指定的模式。考虑某些模式希望区分大小写而某些模式不希望区分大小写的情况:不区分大小写的模式只需要在模式前面包含 (?i)
。例如
$pattern = "foobar";
if ( /$pattern/i ) { }
# more flexible:
$pattern = "(?i)foobar";
if ( /$pattern/ ) { }
这些修饰符在封闭组的末尾还原。例如,
( (?i) blah ) \s+ \g1
将匹配任何情况下的 blah
、一些空格以及前一个单词的完全(包括大小写!)重复,假设使用 /x
修饰符,并且此组外部没有 /i
修饰符。
这些修饰符不会传递到封闭组中调用的命名子模式。换句话说,诸如 ((?i)(?&NAME))
的模式不会更改 NAME 模式的区分大小写。
修饰符会被同一作用域中包含相同修饰符的此构造的后续出现覆盖,因此
/((?im)foo(?-m)bar)/
不区分大小写地匹配所有 foobar
,但仅对 foo
部分使用 /m
规则。"a"
标志也会覆盖 aa
;同样,aa
也会覆盖 "a"
。"x"
和 xx
也是如此。因此,在
/(?-x)foo/xx
匹配 foo
时,/x
和 /xx
都会关闭。而在
/(?x)foo/x
匹配 foo
时,/x
启用,但 /xx
不启用。(人们可能会错误地认为,由于内部 (?x)
已经在 /x
的作用域中,因此结果实际上是它们的总和,产生 /xx
。它不是这样工作的。)类似地,执行诸如 (?xx-x)foo
之类操作会关闭匹配 foo
的所有 "x"
行为,而不是从 2 中减去 1 个 "x"
以获得 1 个剩余的 "x"
。
可以在 use re
的作用域内编译的所有正则表达式中全局设置这些修饰符中的任何一个。请参阅 "'/flags' mode" in re。
从 Perl 5.14 开始,"?"
之后的 "^"
(插入符号或扬抑符)是 d-imnsx
的简写等效项。标志("d"
除外)可以跟随插入符号以覆盖它。但减号与它不合法。
请注意,"a"
、"d"
、"l"
、"p"
和 "u"
修饰符很特殊,因为它们只能启用,不能禁用,并且 "a"
、"d"
、"l"
和 "u"
修饰符是互斥的:指定一个会取消指定其他修饰符,并且构造中最多可以出现一个(或两个 "a"
)。因此,例如,(?-p)
在 use warnings
下编译时会发出警告;(?-d:...)
和 (?dl:...)
是致命错误。
另请注意,"p"
修饰符很特殊,因为它在模式中的任何位置出现都会产生全局效果。
没有修饰符会使其成为无操作(因此,除非是生成代码,否则你为什么要指定它),并且从 v5.30 开始,在 use re 'strict'
下发出警告。
(?:pattern)
(?adluimnsx-imnsx:pattern)
(?^aluimnsx:pattern)
这用于聚类,而不是捕获;它对子表达式进行分组,如 "()"
,但不会像 "()"
那样进行反向引用。因此
@fields = split(/\b(?:a|b|c)\b/)
与
@fields = split(/\b(a|b|c)\b/)
匹配相同的字段分隔符,但不会将分隔符本身作为额外的字段输出(即使这是 "split" in perlfunc 的行为,当其模式包含捕获组时)。如果你不需要捕获字符,那么不捕获字符也会更便宜。
"?"
和 ":"
之间的任何字母都充当标志修饰符,就像 (?adluimnsx-imnsx)
一样。例如,
/(?s-i:more.*than).*million/i
等效于更冗长的
/(?:(?s-i)more.*than).*million/i
请注意,此构造中包含的任何 ()
构造仍将捕获,除非 /n
修饰符有效。
与 "(?adlupimnsx-imnsx)" 构造类似,aa
和 "a"
相互覆盖,xx
和 "x"
也是如此。它们不是相加的。因此,执行类似 (?xx-x:foo)
的操作会关闭匹配 foo
的所有 "x"
行为。
从 Perl 5.14 开始,"?"
后面的 "^"
(插入符号或扬抑符)是 d-imnsx
的简写等效项。任何正标志(除了 "d"
)都可以跟随插入符号,因此
(?^x:foo)
等效于
(?x-imns:foo)
插入符号告诉 Perl,此簇不会继承任何周围模式的标志,而是使用系统默认值(d-imnsx
),由指定的任何标志修改。
插入符号允许对已编译正则表达式进行更简单的字符串化。这些看起来像
(?^:pattern)
任何非默认标志都出现在插入符号和冒号之间。因此,查看此类字符串化的测试不需要在其中硬编码系统默认标志,只需插入符号即可。如果向 Perl 添加了新标志,插入符号扩展的含义将更改为包括这些标志的默认值,因此测试仍将有效,不会更改。
在插入符号后指定负标志是一个错误,因为该标志是冗余的。
(?^...)
的助记符:一个新的开始,因为插入符号的通常用法是在开头匹配。
(?|pattern)
这是“分支重置”模式,它具有一个特殊属性,即捕获组从每个交替分支的相同起始点开始编号。它从 perl 5.10.0 开始可用。
捕获组从左到右编号,但在该构造内,每个分支的编号都会重新开始。
每个分支内的编号将是正常的,并且该构造后的任何组将被编号,就好像该构造只包含一个分支,即其中捕获组最多的那个。
当您想要捕获多个备选匹配项之一时,该构造非常有用。
考虑以下模式。下面的数字显示捕获的内容将存储在哪个组中。
# before ---------------branch-reset----------- after
/ ( a ) (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
# 1 2 2 3 2 3 4
在将分支重置模式与命名捕获结合使用时要小心。命名捕获被实现为对保存捕获的编号组的别名,并且这会干扰分支重置模式的实现。如果您在分支重置模式中使用命名捕获,最好在每个交替中使用相同的名称,并按相同的顺序
/(?| (?<a> x ) (?<b> y )
| (?<a> z ) (?<b> w )) /x
如果不这样做可能会产生意外
"12" =~ /(?| (?<a> \d+ ) | (?<b> \D+))/x;
say $+{a}; # Prints '12'
say $+{b}; # *Also* prints '12'.
这里的问题是名为 a
的组和名为 b
的组都是属于 $1
的组的别名。
环视断言是零宽模式,它匹配特定模式而不将其包含在 $&
中。当其子模式匹配时,正断言匹配,当其子模式失败时,负断言匹配。后向环视匹配直到当前匹配位置的文本,前向环视匹配当前匹配位置之后的文本。
(?=pattern)
(*pla:pattern)
(*positive_lookahead:pattern)
零宽正向先行断言。例如,/\w+(?=\t)/
匹配一个单词后跟一个制表符,但不将制表符包含在 $&
中。
(?!pattern)
(*nla:pattern)
(*negative_lookahead:pattern)
零宽负向先行断言。例如,/foo(?!bar)/
匹配任何不后跟“bar”的“foo”出现。但请注意,先行和后顾并不是同一回事。您不能将此用于后顾。
如果您正在寻找一个不前置“foo”的“bar”,则 /(?!foo)bar/
将不会执行您想要的操作。这是因为 (?!foo)
只是表示下一件事不能是“foo”——而它不是,它是一个“bar”,所以“foobar”将匹配。改为使用后顾(见下文)。
(?<=pattern)
\K
(*plb:pattern)
(*positive_lookbehind:pattern)
零宽正向后顾断言。例如,/(?<=\t)\w+/
匹配一个后跟制表符的单词,但不将制表符包含在 $&
中。
在 Perl 5.30 之前,它仅适用于固定宽度的后顾,但从该版本开始,它可以作为一项实验性功能处理 1 到 255 个字符的可变长度。如果您使用可变长度正向后顾断言,则会自动启用该功能。
在 Perl 5.35.10 中,此构造的实验性质的范围已缩小,并且仅当构造包含捕获括号时才会产生实验性警告。除非关闭,否则将在模式编译时发出警告,在 experimental::vlb
类别中。这是为了警告您,可变长度正向后顾中捕获缓冲区的精确内容未定义,并且可能会在 perl 的未来版本中更改。
目前,如果您在正向可变长度后顾中使用捕获缓冲区,则结果将是最长且最左边的可能匹配。这意味着
"aax" =~ /(?=x)(?<=(a|aa))/
"aax" =~ /(?=x)(?<=(aa|a))/
"aax" =~ /(?=x)(?<=(a{1,2}?)/
"aax" =~ /(?=x)(?<=(a{1,2})/
都将导致 $1
包含 "aa"
。在 perl 的未来版本中,我们有可能更改此行为。
此构造有一种特殊形式,称为 \K
(自 Perl 5.10.0 起可用),它会导致正则表达式引擎“保留”它在 \K
之前匹配的所有内容,而不将其包含在 $&
中。这有效地提供了任何长度的非实验性可变长度后顾。
而且,有一种技术可以用来处理早期版本中可变长度的后向引用,并且长度超过 255 个字符。它在 http://www.drregex.com/2019/02/variable-length-lookbehinds-actually.html 中进行了描述。
请注意,在 /i
下,几个单字符匹配两个或三个其他字符。这使得它们具有可变长度,并且 255 的长度适用于匹配中的最大字符数。例如,qr/\N{LATIN SMALL LETTER SHARP S}/i
匹配序列 "ss"
。在 /i
下,你的后向引用断言可能包含 127 个 Sharp S 字符,但添加第 128 个字符会生成一个编译错误,因为它可能匹配一行中 256 个 "s"
字符。
允许在另一个环视断言内部使用 \K
,但目前的行为尚未明确定义。
由于各种原因,\K
可能比等效的 (?<=...)
构造显着高效,并且在希望有效删除字符串中某项内容后跟另一项内容的情况下特别有用。例如
s/(foo)bar/$1/g;
可以重写为效率更高的
s/foo\Kbar//g;
如果非贪婪修饰符 "?"
在构造内的捕获组中,则可能无法获得预期的结果。
(?<!pattern)
(*nlb:pattern)
(*negative_lookbehind:pattern)
一个零宽度的负后向引用断言。例如,/(?<!bar)foo/
匹配任何不跟在 "bar" 之后的 "foo" 的出现。
在 Perl 5.30 之前,它仅适用于固定宽度的后向引用,但从该版本开始,它可以将可变长度从 1 到 255 个字符作为实验性功能来处理。如果你使用可变长度的负后向引用断言,则该功能会自动启用。
在 Perl 5.35.10 中,此构造的实验性质的范围已缩小,并且仅当构造包含捕获括号时才会产生实验性警告。除非在 experimental::vlb
类别中关闭,否则警告将在模式编译时发出。这是为了警告你,可变长度负后向引用中捕获缓冲区的准确内容尚未明确定义,并且可能会在 perl 的未来版本中发生更改。
目前,如果你在负可变长度后向引用中使用捕获缓冲区,结果可能不是你所期望的,例如
say "axfoo"=~/(?=foo)(?<!(a|ax)(?{ say $1 }))/ ? "y" : "n";
将输出以下内容
a
no
这没有意义,因为这应该输出 "ax",因为 "a" 没有对齐在正确的位置。另一个例子是
say "yes: '$1-$2'" if "aayfoo"=~/(?=foo)(?<!(a|aa)(a|aa)x)/;
将输出以下内容
yes: 'aa-a'
在 perl 的未来版本中,我们可能会更改此行为,以便这两个示例都产生更合理的结果。
请注意,我们确信该构造将匹配并适当地拒绝模式,未定义的行为严格与匹配期间或之后的捕获缓冲区的值相关。
有一种技术可用于处理早期版本和长度超过 255 个字符的可变长度后向引用。它在 http://www.drregex.com/2019/02/variable-length-lookbehinds-actually.html 中进行了描述。
请注意,在 /i
下,几个单字符匹配两个或三个其他字符。这使得它们具有可变长度,并且 255 的长度适用于匹配中的最大字符数。例如,qr/\N{LATIN SMALL LETTER SHARP S}/i
匹配序列 "ss"
。在 /i
下,你的后向引用断言可能包含 127 个 Sharp S 字符,但添加第 128 个字符会生成一个编译错误,因为它可能匹配一行中 256 个 "s"
字符。
如果非贪婪修饰符 "?"
在构造内的捕获组中,则可能无法获得预期的结果。
(?<NAME>pattern)
(?'NAME'pattern)
一个命名的捕获组。在各个方面都与普通捕获括号 ()
相同,但附加的事实是该组可以在各种正则表达式构造(如 \g{NAME}
)中通过名称引用,并且可以在成功匹配后通过 %+
或 %-
按名称访问。有关 %+
和 %-
哈希的更多详细信息,请参见 perlvar。
如果多个不同的捕获组具有相同的名称,则 $+{NAME}
将引用匹配中最左边的已定义组。
形式 (?'NAME'pattern)
和 (?<NAME>pattern)
是等效的。
注意:虽然此构造的表示法与 .NET 正则表达式中类似的功能相同,但行为不同。在 Perl 中,无论是否命名,组都按顺序编号。因此,在模式中
/(x)(?<foo>y)(z)/
$+{foo}
将与 $2
相同,而 $3
将包含 'z',而不是 .NET 正则表达式黑客可能期望的相反内容。
目前,NAME 仅限于简单的标识符。换句话说,它必须匹配 /^[_A-Za-z][_A-Za-z0-9]*\z/
或其 Unicode 扩展(请参见 utf8),尽管它没有被语言环境扩展(请参见 perllocale)。
注意:为了让具有 Python 或 PCRE 正则表达式引擎经验的程序员更容易理解,可以使用模式 (?P<NAME>pattern)
代替 (?<NAME>pattern)
;但是,此形式不支持使用单引号作为名称的分隔符。
\k<NAME>
\k'NAME'
\k{NAME}
命名反向引用。类似于数字反向引用,不同之处在于组由名称指定,而不是数字。如果多个组具有相同的名称,则它引用当前匹配中定义的最左组。
引用模式中前面未通过 (?<NAME>)
定义的名称是错误的。
所有三种形式都是等效的,尽管使用 \k{ NAME }
时,你可以在大括号内但紧邻大括号处选择性地留有空格,如所示。
注意:为了让具有 Python 或 PCRE 正则引擎经验的程序员更轻松,可以使用模式 (?P=NAME)
代替 \k<NAME>
。
(?{ code })
警告:安全地使用此功能需要你了解其限制。执行具有副作用的代码可能不会在版本之间执行得完全相同,因为正则引擎中未来的优化会产生影响。有关此内容的更多信息,请参见 "嵌入式代码执行频率"。
此零宽断言执行任何嵌入式 Perl 代码。它始终成功,其返回值设置为 $^R
。
在文字模式中,代码与周围代码同时解析。在模式内,控制权将暂时传递回 perl 解析器,直到遇到逻辑平衡的结束大括号。这类似于处理文字字符串中的数组索引表达式的做法,例如
"abc$array[ 1 + f('[') + g()]def"
特别是,大括号不需要平衡
s/abc(?{ f('{'); })/def/
即使在运行时内插和编译的模式中,文字代码块也会在 perl 编译时编译一次;以下内容打印 "ABCD"
print "D";
my $qr = qr/(?{ BEGIN { print "A" } })/;
my $foo = "foo";
/$foo$qr(?{ BEGIN { print "B" } })/;
BEGIN { print "C" }
在代码文本从运行时信息派生而不是直接出现在源代码 /模式/ 中的模式中,代码在模式编译时同时编译,出于安全原因,use re 'eval'
必须在作用域中。这是为了阻止包含代码片段的用户提供的模式被执行。
在需要使用 use re 'eval'
启用此功能的情况下,如果你的 perl 支持,还应启用污点检查。更好的做法是,在安全隔间内使用经过仔细约束的评估。有关这两个机制的详细信息,请参见 perlsec。
从解析、词法变量作用域和闭包的角度来看,
/AAA(?{ BBB })CCC/
表现得大约像
/AAA/ && do { BBB } && /CCC/
类似地,
qr/AAA(?{ BBB })CCC/
表现得大约像
sub { /AAA/ && do { BBB } && /CCC/ }
特别是
{ my $i = 1; $r = qr/(?{ print $i })/ }
my $i = 2;
/$r/; # prints "1"
在 (?{...})
块内,$_
引用正则表达式匹配的字符串。你还可以使用 pos()
了解匹配在此字符串中的当前位置。
代码块从词法变量声明的角度引入一个新的作用域,但不从local
和类似的本地化行为的角度引入。因此,同一模式中的后续代码块仍将看到在早期块中本地化的值。这些累积的本地化在成功匹配结束时或断言回溯时撤消(比较"回溯")。例如,
$_ = 'a' x 8;
m<
(?{ $cnt = 0 }) # Initialize $cnt.
(
a
(?{
local $cnt = $cnt + 1; # Update $cnt,
# backtracking-safe.
})
)*
aaaa
(?{ $res = $cnt }) # On success copy to
# non-localized location.
>x;
最初会将$cnt
递增到 8;然后在回溯期间,它的值将被解开到 4,这是分配给$res
的值。在正则表达式执行结束时,$cnt
将被解回到其初始值 0。
此断言可用作
(?(condition)yes-pattern|no-pattern)
switch 中的条件。如果不以这种方式使用,则code的评估结果将放入特殊变量$^R
中。这会立即发生,因此可以在同一正则表达式中的其他(?{ code })
断言中使用$^R
。
对$^R
的赋值在上面被正确地本地化,因此如果断言被回溯,则$^R
的旧值将被恢复;比较"回溯"。
请注意,特殊变量$^N
与代码块特别有用,可以在变量中捕获子匹配的结果,而无需跟踪嵌套括号的数量。例如
$_ = "The brown fox jumps over the lazy dog";
/the (\S+)(?{ $color = $^N }) (\S+)(?{ $animal = $^N })/i;
print "color = $color, animal = $animal\n";
使用此构造在模式中全局禁用一些优化,因此模式可能会因此执行得慢得多。使用*
代替?
块以创建此构造的乐观形式。(*{ ... })
不应禁用任何优化。
(*{ code })
这与(?{ code })
完全相同,但它不会在正则表达式引擎中禁用任何优化。它执行的频率可能因 Perl 版本而异。在失败的匹配中,它甚至可能根本不会执行。
(??{ code })
警告:安全地使用此功能需要你了解其限制。执行具有副作用的代码可能不会在版本之间执行得完全相同,因为正则引擎中未来的优化会产生影响。有关此内容的更多信息,请参见 "嵌入式代码执行频率"。
这是一个“延迟”的正则子表达式。它的行为与上面描述的(?{ code })
代码块完全相同,除了它的返回值不会分配给$^R
,而是被视为一个模式(如果它是一个字符串,则编译;如果它是一个 qr// 对象,则按原样使用),然后匹配它,就好像它被插入到此构造中一样。
在此子模式匹配期间,它有自己的一组捕获,这些捕获在子匹配期间有效,但一旦控制权返回主模式,它们就会被丢弃。例如,以下匹配,其中内部模式捕获“B”并匹配“BB”,而外部模式捕获“A”;
my $inner = '(.)\1';
"ABBA" =~ /^(.)(??{ $inner })\1/;
print $1; # prints "A";
请注意,这意味着内部模式无法引用外部定义的捕获组。(代码块本身可以使用$1
、等来引用封闭模式的捕获组。)因此,尽管
('a' x 100)=~/(??{'(.)' x 100})/
将匹配,但它不会在退出时设置$1
。
以下模式匹配括号组
$re = qr{
\(
(?:
(?> [^()]+ ) # Non-parens without backtracking
|
(??{ $re }) # Group with matching parens
)*
\)
}x;
另请参阅 (?PARNO)
,了解完成相同任务的另一种更有效的方法。
在不使用任何输入字符串的情况下执行过多的延迟正则表达式也将导致致命错误。发生这种情况的深度已编译到 perl 中,因此可以通过自定义构建来更改它。
使用此构造会全局禁用模式中的一些优化,因此模式执行速度可能会因此变慢很多。
(?PARNO)
(?-PARNO)
(?+PARNO)
(?R)
(?0)
递归子模式。将当前模式中给定捕获缓冲区的内容视为独立子模式,并尝试在字符串的当前位置匹配它。调用者捕获状态的信息(如反向引用)可供子模式使用,但子模式设置的捕获缓冲区对调用者不可见。
类似于 (??{ code })
,但它不涉及执行任何代码或可能编译返回的模式字符串;相反,它将指定捕获组中包含的当前模式部分视为必须在当前位置匹配的独立模式。另一个不同之处是捕获缓冲区的处理,与 (??{ code })
不同,递归模式可以访问其调用者的匹配状态,因此可以安全地使用反向引用。
PARNO 是一系列数字(不以 0 开头),其值反映要递归到的捕获组的 paren-number。(?R)
递归到整个模式的开头。(?0)
是 (?R)
的备用语法。如果 PARNO 前面有加号或减号,则假定它是相对的,负数表示前面的捕获组,正数表示后面的捕获组。因此,(?-1)
指的是最近声明的组,而 (?+1)
指的是要声明的下一个组。请注意,相对递归的计数与相对反向引用的计数不同,因为在递归中未关闭的组是包含在内的。
以下模式匹配一个函数 foo()
,其可能包含平衡的圆括号作为参数。
$re = qr{ ( # paren group 1 (full function)
foo
( # paren group 2 (parens)
\(
( # paren group 3 (contents of parens)
(?:
(?> [^()]+ ) # Non-parens without backtracking
|
(?2) # Recurse to start of paren group 2
)*
)
\)
)
)
}x;
如果模式如下使用
'foo(bar(baz)+baz(bop))'=~/$re/
and print "\$1 = $1\n",
"\$2 = $2\n",
"\$3 = $3\n";
产生的输出应如下
$1 = foo(bar(baz)+baz(bop))
$2 = (bar(baz)+baz(bop))
$3 = bar(baz)+baz(bop)
如果没有定义相应的捕获组,则会发生致命错误。在不消耗任何输入字符串的情况下深度递归也会导致致命错误。发生这种情况的深度编译到 perl 中,因此可以使用自定义构建对其进行更改。
以下内容显示了如何使用负索引可以更轻松地在 qr//
构造中嵌入递归模式,以便以后使用
my $parens = qr/(\((?:[^()]++|(?-1))*+\))/;
if (/foo $parens \s+ \+ \s+ bar $parens/x) {
# do something here...
}
注意,此模式的行为与相同形式的等效 PCRE 或 Python 构造不同。在 Perl 中,你可以回溯到递归组中,在 PCRE 和 Python 中,递归到组中被视为原子。此外,修饰符在编译时解析,因此诸如 (?i:(?1))
或 (?:(?i)(?1))
之类的构造不会影响子模式的处理方式。
(?&NAME)
递归到一个命名的子模式。与 (?PARNO)
相同,不同之处在于递归到的括号由名称确定。如果多个括号具有相同的名称,则递归到最左边的括号。
引用模式中未声明的名称是错误的。
注意:为了让具有 Python 或 PCRE 正则引擎经验的程序员更轻松,可以使用模式 (?P>NAME)
代替 (?&NAME)
。
(?(condition)yes-pattern|no-pattern)
(?(condition)yes-pattern)
条件表达式。如果 condition 产生真值,则匹配 yes-pattern,否则匹配 no-pattern。缺失的模式始终匹配。
(condition)
应为以下之一
(如果相应的括号对匹配,则有效);
(如果具有给定名称的组匹配,则有效);
(R)
(在递归或 eval 中评估时为真)。此外,"R"
后面可以跟一个数字(在适当的组内递归时评估为真),或跟 &NAME
,在这种情况下,仅在以命名的组进行递归时评估为真。
以下是可能的谓词的摘要
(1)
(2)
...检查编号的捕获组是否匹配了某些内容。完整语法:(?(1)then|else)
(<NAME>)
('NAME')
检查具有给定名称的组是否匹配了某些内容。完整语法:(?(<name>)then|else)
(?=...)
(?!...)
(?<=...)
(?<!...)
检查模式是否匹配(或不匹配,对于 "!"
变体)。完整语法:(?(?=lookahead)then|else)
(?{ CODE })
将代码块的返回值视为条件。完整语法:(?(?{ CODE })then|else)
请注意,使用此结构可能会全局影响模式的性能。考虑使用 (*{ CODE })
(*{ CODE })
将代码块的返回值视为条件。完整语法:(?(*{ CODE })then|else)
(R)
检查表达式是否已在递归中评估。完整语法:(?(R)then|else)
(R1)
(R2)
...检查表达式是否在第 n 个捕获组中直接执行时评估。此检查是正则表达式的等效项
if ((caller(0))[3] eq 'subname') { ... }
换句话说,它不检查完整的递归堆栈。
完整语法:(?(R1)then|else)
(R&NAME)
类似于 (R1)
,此谓词检查我们是否直接在具有给定名称的最左组中执行(这是 (?&NAME)
用于消除歧义的相同逻辑)。它不检查完整的堆栈,而只检查最内层活动递归的名称。完整语法:(?(R&name)then|else)
(DEFINE)
在这种情况下,yes-pattern 永远不会直接执行,并且不允许 no-pattern。与 (?{0})
类似,但效率更高。有关详细信息,请参见下文。完整语法:(?(DEFINE)definitions...)
例如
m{ ( \( )?
[^()]+
(?(1) \) )
}x
匹配一段非括号,可能包含在括号中。
一种特殊形式是 (DEFINE)
谓词,它永远不会直接执行其 yes-pattern,并且不允许 no-pattern。这允许定义仅通过递归机制执行的子模式。通过这种方式,您可以定义一组正则表达式规则,这些规则可以捆绑到您选择的任何模式中。
建议您将 DEFINE 块放在模式的末尾,并为其中定义的任何子模式命名。
此外,值得注意的是,以这种方式定义的模式可能效率不高,因为优化器在处理它们时不太聪明。
以下是如何使用它的一个示例
/(?<NAME>(?&NAME_PAT))(?<ADDR>(?&ADDRESS_PAT))
(?(DEFINE)
(?<NAME_PAT>....)
(?<ADDRESS_PAT>....)
)/x
请注意,在递归返回后无法访问递归内部匹配的捕获组,因此需要额外的捕获组层。因此,即使 $+{NAME}
已定义,$+{NAME_PAT}
也不会被定义。
最后,请记住,在 DEFINE 块内创建的子模式计入捕获的绝对数量和相对数量,因此
my @captures = "a" =~ /(.) # First capture
(?(DEFINE)
(?<EXAMPLE> 1 ) # Second capture
)/x;
say scalar @captures;
将输出 2,而不是 1。如果您打算使用 qr//
运算符编译定义,然后在另一个模式中内插它们,这一点尤其重要。
(?>pattern)
(*atomic:pattern)
一个“独立”的子表达式,它匹配一个独立的 pattern 在给定位置锚定时匹配的子字符串,并且它匹配除了这个子字符串之外的任何内容。此构造对于优化本来是“永恒”的匹配非常有用,因为它不会回溯(请参见“回溯”)。它也可能在需要“尽可能多地获取,并且不放弃任何内容”语义的地方有用。
例如:^(?>a*)ab
永远不会匹配,因为 (?>a*)
(锚定在字符串开头,如上所述)将匹配字符串开头所有字符 “a”
,没有留下 “a”
给 ab
匹配。相比之下,a*ab
将与 a+b
匹配,因为子组 a*
的匹配受后续组 ab
的影响(请参见“回溯”)。特别是,a*ab
中的 a*
将匹配比独立的 a*
更少的字符,因为这使得尾部匹配。
(?>pattern)
在匹配后不会完全禁用回溯。仍然可以回溯到构造之后,但不能回溯到构造之中。因此,((?>a*)|(?>b*))ar
仍然会匹配“bar”。
类似于 (?>pattern)
的效果可以通过编写 (?=(pattern))\g{-1}
来实现。这与独立的 a+
匹配相同的子字符串,并且随后的 \g{-1}
会消耗匹配的字符串;因此,它将零长度断言变成 (?>...)
的类似物。(这两种结构之间的区别在于,第二个结构使用捕获组,从而改变了正则表达式其余部分中反向引用的序数。)
考虑以下模式
m{ \(
(
[^()]+ # x+
|
\( [^()]* \)
)+
\)
}x
这将有效地匹配一个非空组,其中匹配的括号深度为两层或更浅。但是,如果没有这样的组,它将在长字符串上几乎永远持续下去。这是因为有许多不同的方法可以将长字符串拆分为多个子字符串。这就是 (.+)+
所做的,并且 (.+)+
类似于上述模式的子模式。考虑上述模式如何在几秒钟内检测到 ((()aaaaaaaaaaaaaaaaaa
上的不匹配,但每个额外的字母都会使这个时间加倍。这种指数性能将使您的程序看起来已挂起。但是,对该模式进行微小的更改
m{ \(
(
(?> [^()]+ ) # change x+ above to (?> x+ )
|
\( [^()]* \)
)+
\)
}x
它使用 (?>...)
在上述模式匹配时完全匹配(自己验证这一点将是一项富有成效的练习),但在用于具有 1000000 个 "a"
的类似字符串时,完成时间为上述模式的四分之一。但是,请注意,当该结构后跟量词时,它当前会在 use warnings
pragma 或 -w 开关下触发一条警告消息,指出它"在正则表达式中多次匹配空字符串"
。
对于简单组,例如模式 (?> [^()]+ )
,可以通过否定前瞻来实现类似的效果,如 [^()]+ (?! [^()] )
。在具有 1000000 个 "a"
的字符串上,这仅慢 4 倍。
在许多情况下,“尽可能多地获取,但不要给予任何回报”语义是可取的,而在这些情况下,乍一看,一个简单的 ()*
看起来像是正确的解决方案。假设我们解析文本,其中注释以 "#"
分隔,后面跟着一些可选的(水平)空白。与它的外观相反,#[ \t]*
不是匹配注释分隔符的正确子表达式,因为它可能会“放弃”一些空白,如果可以这样匹配模式的其余部分。正确的答案是其中之一
(?>#[ \t]*)
#[ \t]*(?![ \t])
例如,要将非空注释获取到 $1
中,应该使用其中之一
/ (?> \# [ \t]* ) ( .+ ) /x;
/ \# [ \t]* ( [^ \t] .* ) /x;
选择哪一个取决于这些表达式中的哪一个更好地反映上述注释规范。
在一些文献中,此结构被称为“原子匹配”或“占有匹配”。
占有量词等效于将它们应用到的项目放在这些结构之一内。应用以下等效项
Quantifier Form Bracketing Form
--------------- ---------------
PAT*+ (?>PAT*)
PAT++ (?>PAT+)
PAT?+ (?>PAT?)
PAT{min,max}+ (?>PAT{min,max})
嵌套的 (?>...)
结构不是无操作,即使乍一看它们似乎是无操作。这是因为嵌套的 (?>...)
可以限制内部回溯,否则可能会发生内部回溯。例如,
"abc" =~ /(?>a[bc]*c)/
匹配,但
"abc" =~ /(?>a(?>[bc]*)c)/
不匹配。
(?[ ])
注意:本节介绍正则表达式行为的抽象近似。有关在可能的选择中选择匹配时涉及的规则的更严格(且复杂)的视图,请参见 "组合 RE 片段"。
正则表达式匹配的一个基本特征涉及称为回溯的概念,目前所有常规非占有表达式量词(即 "*"
、*?
、"+"
、+?
、{n,m}
和 {n,m}?
)都在需要时使用该概念。回溯通常在内部进行优化,但此处概述的一般原则有效。
对于正则表达式匹配,整个正则表达式必须匹配,而不仅仅是其中的一部分。因此,如果包含量词的模式的开头以导致模式中后面的部分失败的方式成功,则匹配引擎会备份并重新计算开头部分——这就是它被称为回溯的原因。
以下是回溯的一个示例:假设您想在字符串“Food is on the foo table.”中找到“foo”后面的单词。
$_ = "Food is on the foo table.";
if ( /\b(foo)\s+(\w+)/i ) {
print "$2 follows $1.\n";
}
当匹配运行时,正则表达式的第一部分 (\b(foo)
) 在字符串的开头找到一个可能的匹配,并将“Foo”加载到 $1
中。但是,一旦匹配引擎看到在 $1
中保存的“Foo”后面没有空格,它就会意识到自己的错误并重新开始,在它进行试探性匹配的地方后面一个字符。这次它一直进行到“foo”的下一个出现。这次完整的正则表达式匹配,您将得到预期的输出“table follows foo”。
有时,最小匹配会很有帮助。想象一下,你想匹配“foo”和“bar”之间的所有内容。最初,你写了类似这样的内容
$_ = "The food is under the bar in the barn.";
if ( /foo(.*)bar/ ) {
print "got <$1>\n";
}
这可能会出乎意料地产生
got <d is under the bar in the >
这是因为.*
是贪婪的,所以你得到了第一个“foo”和最后一个“bar”之间的所有内容。这里更有效的方法是使用最小匹配来确保你得到一个“foo”和此后的第一个“bar”之间的文本。
if ( /foo(.*?)bar/ ) { print "got <$1>\n" }
got <d is under the >
这里有另一个示例。假设你想匹配字符串末尾的一个数字,并且还想保留匹配的前一部分。所以你写了这个
$_ = "I have 2 numbers: 53147";
if ( /(.*)(\d*)/ ) { # Wrong!
print "Beginning is <$1>, number is <$2>.\n";
}
这根本不起作用,因为.*
是贪婪的,它吞掉了整个字符串。由于\d*
可以匹配空字符串,因此整个正则表达式匹配成功。
Beginning is <I have 2 numbers: 53147>, number is <>.
这里有一些变体,其中大多数不起作用
$_ = "I have 2 numbers: 53147";
@pats = qw{
(.*)(\d*)
(.*)(\d+)
(.*?)(\d*)
(.*?)(\d+)
(.*)(\d+)$
(.*?)(\d+)$
(.*)\b(\d+)$
(.*\D)(\d+)$
};
for $pat (@pats) {
printf "%-12s ", $pat;
if ( /$pat/ ) {
print "<$1> <$2>\n";
} else {
print "FAIL\n";
}
}
这将打印
(.*)(\d*) <I have 2 numbers: 53147> <>
(.*)(\d+) <I have 2 numbers: 5314> <7>
(.*?)(\d*) <> <>
(.*?)(\d+) <I have > <2>
(.*)(\d+)$ <I have 2 numbers: 5314> <7>
(.*?)(\d+)$ <I have 2 numbers: > <53147>
(.*)\b(\d+)$ <I have 2 numbers: > <53147>
(.*\D)(\d+)$ <I have 2 numbers: > <53147>
正如你所看到的,这可能有点棘手。重要的是要认识到正则表达式仅仅是一组断言,它给出了成功的定义。对于特定字符串,可能会有 0、1 或多种不同的方式来满足定义。如果有多种成功方式,你需要了解回溯才能知道你会实现哪种成功的变体。
在使用超前断言和否定时,这一切都可能变得更加棘手。想象一下,你想找到一个非数字序列,后面不跟着“123”。你可以尝试写成
$_ = "ABC123";
if ( /^\D*(?!123)/ ) { # Wrong!
print "Yup, no 123 in $_\n";
}
但这不会匹配;至少,不是你希望的方式。它声称字符串中没有 123。以下是为什么该模式匹配,这与流行的预期相反的更清晰的图片
$x = 'ABC123';
$y = 'ABC445';
print "1: got $1\n" if $x =~ /^(ABC)(?!123)/;
print "2: got $1\n" if $y =~ /^(ABC)(?!123)/;
print "3: got $1\n" if $x =~ /^(\D*)(?!123)/;
print "4: got $1\n" if $y =~ /^(\D*)(?!123)/;
这打印
2: got ABC
3: got AB
4: got ABC
你可能期望测试 3 失败,因为它看起来是测试 1 的更通用版本。它们之间重要的区别在于,测试 3 包含一个量词 (\D*
),因此可以使用回溯,而测试 1 则不能。发生的事情是你询问“在$x
的开头,在 0 个或更多个非数字之后,你是否有一些不是 123 的东西?”如果模式匹配器让\D*
扩展到“ABC”,这将导致整个模式失败。
搜索引擎最初会将\D*
与“ABC”匹配。然后它将尝试将(?!123)
与“123”匹配,这将失败。但是,由于正则表达式中使用了量词 (\D*
),因此搜索引擎可以回溯并以不同的方式重试匹配,以匹配完整的正则表达式。
该模式真的、真的想要成功,因此它使用了标准模式后退并重试,并允许 \D*
此次仅扩展为“AB”。现在“AB”后面确实有不是“123”的内容。它就是“C123”,这就足够了。
我们可以通过同时使用断言和否定来处理此问题。我们说 $1
中的第一部分必须同时后跟一个数字和不是“123”的内容。请记住,先行断言是零宽表达式——它们只查看,但不消耗其匹配中的任何字符串。因此,以这种方式重写会产生您期望的结果;也就是说,用例 5 将失败,但用例 6 将成功
print "5: got $1\n" if $x =~ /^(\D*)(?=\d)(?!123)/;
print "6: got $1\n" if $y =~ /^(\D*)(?=\d)(?!123)/;
6: got ABC
换句话说,彼此相邻的两个零宽断言的作用就像它们被 AND 运算一样,就像您使用任何内置断言一样:/^$/
仅当您同时位于行首 AND 行尾时才匹配。更深层次的基本原理是,正则表达式中的并置始终表示 AND,除非您使用竖线编写显式 OR。/ab/
表示匹配“a”AND(然后)匹配“b”,尽管尝试匹配是在不同位置进行的,因为“a”不是零宽断言,而是一宽断言。
警告:特别复杂的正则表达式可能需要指数时间才能解决,因为它们可以使用回溯尝试匹配的可能方式非常多。例如,如果没有正则表达式引擎进行内部优化,这将需要非常长的时间才能运行
'aaaaaaaaaaaa' =~ /((a{0,5}){0,5})*[c]/
如果您在内部组中使用了 "*"
,而不是将它们限制为 0 到 5 次匹配,那么它将永远运行下去——或者直到您用尽堆栈空间。此外,这些内部优化并不总是适用的。例如,如果您在外部组中放置 {0,5}
而不是 "*"
,则没有当前适用的优化,并且匹配需要很长时间才能完成。
优化此类庞然大物的强大工具称为“独立组”,它不会回溯(参见 "(?>pattern)"
)。还要注意,零长度先行/后行断言不会回溯以匹配尾部,因为它们处于“逻辑”上下文中:只有它们是否匹配才被认为是相关的。有关先行副作用可能影响后续匹配的示例,请参见 "(?>pattern)"
。
脚本运行基本上是一系列字符,全部来自同一 Unicode 脚本(参见 perlunicode 中的“脚本”),例如拉丁语或希腊语。在大多数地方,一个单词永远不会用多个脚本书写,除非是欺骗攻击。一个臭名昭著的例子是
paypal.com
这些字母可以全部是拉丁字母(如上例所示),也可以全部是西里尔字母(点除外),或者可以是两者混合。对于互联网地址,.com
将使用拉丁字母,任何西里尔字母都会导致它成为混合,而不是脚本运行。点击此类链接的人不会被定向到真正的 Paypal 网站,但攻击者会制作一个类似的网站来尝试从该人那里收集敏感信息。
从 Perl 5.28 开始,现在可以轻松检测到不是脚本运行的字符串。只需将几乎任何模式都包含在其中,如下所示
(*script_run:pattern)
(*sr:pattern)
发生的情况是,在模式成功匹配后,它会受到附加条件的约束,即其中的每个字符都必须来自同一脚本(见下文例外)。如果不是这样,则会发生回溯,直到找到匹配的所有内容都在同一脚本中,或者所有可能性都已用尽。这可能会导致大量回溯,但通常,只有恶意输入才会导致这种情况,尽管速度下降可能会导致拒绝服务攻击。如果您的需要允许,最好使模式原子化以减少回溯量。这很可能是您想要的,而不是编写此内容
(*script_run:(?>pattern))
您可以编写以下内容
(*atomic_script_run:pattern)
(*asr:pattern)
(参见 "(?>pattern)"
。)
在台湾、日本和韩国,文本通常混合了来自其本国脚本和基础中文的字符。Perl 遵循 Unicode 的 UTS 39 (https://unicode.org/reports/tr39/)Unicode 安全机制来允许此类混合。例如,日语脚本片假名和平假名在实践中通常混合在一起,还有一些汉字,因此 Perl 将其视为单个脚本运行。
用于匹配十进制数字的规则稍微严格一些。许多脚本都有自己的数字集,相当于西方的 0
到 9
。少数,如阿拉伯语,有多个集合。对于一个字符串被认为是脚本运行,其中的所有数字都必须来自同一组十个,由遇到的第一个数字确定。例如,
qr/(*script_run: \d+ \b )/x
保证匹配的数字都来自同一组 10 个数字。不会从具有与实际值不同的值的不同脚本中获取相似的数字。
Unicode 有三个伪脚本,需要特殊处理。
"Unknown" 应用于尚未确定其含义的代码点。Perl 目前将匹配作为脚本运行,任何由这些代码点之一组成的单个字符字符串。但是,任何包含这些代码点之一且长度超过一个代码点的字符串都不会被视为脚本运行。
"Inherited" 应用于修改其他字符的字符,例如某种类型的重音。这些字符被视为主字符的脚本中,因此永远不会导致脚本运行不匹配。
另一个是“Common”。它主要由标点符号、表情符号、数学和音乐中使用的字符、ASCII 数字 0
到 9
以及这些数字的全宽形式组成。这些字符可以出现在世界上许多脚本的文本中。这些字符也不会导致脚本运行不匹配。但与其他脚本一样,运行中的所有数字都必须来自同一组 10 个数字。
此结构是非捕获的。可以在 pattern 中添加括号以进行捕获(如果需要)。如果你计划使用 "(*ACCEPT) (*ACCEPT:arg)" 并且不希望它绕过脚本运行检查,则必须执行此操作。
UTS 39 (https://unicode.org/reports/tr39/) 修改的 Script_Extensions
属性用作此功能的基础。
总结一下,
所有长度为 0 或长度为 1 的序列都是脚本运行。
当且仅当满足所有以下条件时,较长的序列才是脚本运行
序列中没有代码点具有 Script_Extension
属性 Unknown
。
这目前意味着序列中的所有代码点都已被 Unicode 分配为不是私有使用或代理代码点的字符。
序列中的所有字符都来自 Common 脚本和/或 Inherited 脚本和/或单个其他脚本。
字符的脚本由 UTS 39 (https://unicode.org/reports/tr39/) 修改后的 Script_Extensions
属性确定,如上所述。
序列中的所有十进制数字都来自同一块 10 个连续数字。
这些特殊模式通常采用 (*VERB:arg)
的形式。除非另有说明,否则 arg 参数是可选的;在某些情况下,它是必需的。
包含允许参数的特殊回溯动词的任何模式都具有特殊行为,即执行时会设置当前包的 $REGERROR
和 $REGMARK
变量。执行此操作时,将应用以下规则
如果失败,则 $REGERROR
变量将设置为动词模式的 arg 值(如果动词参与了匹配失败)。如果省略了模式的 arg 部分,则 $REGERROR
将设置为执行的最后一个 (*MARK:NAME)
模式的名称,如果没有,则设置为 TRUE。此外,$REGMARK
变量将设置为 FALSE。
如果匹配成功,则 $REGERROR
变量将设置为 FALSE,并且 $REGMARK
变量将设置为执行的最后一个 (*MARK:NAME)
模式的名称。有关更多详细信息,请参阅以下 (*MARK:NAME)
动词的说明。
注意: $REGERROR
和 $REGMARK
不是像 $1
和大多数其他与正则表达式相关的变量那样的魔术变量。它们不是特定于作用域的,也不是只读的,而是类似于 $AUTOLOAD
的不稳定包变量。它们是在包含执行正则表达式的代码的包中设置的(而不是在编译代码的包中设置的,如果它们不同的话)。如有必要,您可以在执行正则表达式之前使用 local
将这些变量的更改本地化到特定作用域。
如果模式不包含允许参数的特殊回溯动词,则根本不会触及 $REGERROR
和 $REGMARK
。
(*PRUNE)
(*PRUNE:NAME)
当回溯到失败时,此零宽度模式会修剪当前点处的回溯树。考虑模式 /A (*PRUNE) B/
,其中 A 和 B 是复杂模式。在达到 (*PRUNE)
动词之前,A 可以根据需要回溯以进行匹配。一旦达到它,匹配就会在 B 中继续,B 也可以根据需要回溯;但是,如果 B 不匹配,则不会进行进一步的回溯,并且模式将在当前起始位置直接失败。
以下示例计算模式中所有可能的匹配字符串(实际上没有匹配任何字符串)。
'aaab' =~ /a+b?(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";
生成
aaab
aaa
aa
a
aab
aa
a
ab
a
Count=9
如果我们在计数前添加一个 (*PRUNE)
,如下所示
'aaab' =~ /a+b?(*PRUNE)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";
我们防止回溯,并像这样查找每个匹配起始点处最长的匹配字符串的计数
aaab
aab
ab
Count=3
模式中可以使用任意数量的 (*PRUNE)
断言。
另请参阅 "(?>pattern)"
和独占量词,了解控制回溯的其他方法。在某些情况下,可以使用 (?>pattern)
替换 (*PRUNE)
,而不会产生功能差异;但是,(*PRUNE)
可用于处理无法仅使用 (?>pattern)
表达的情况。
(*SKIP)
(*SKIP:NAME)
此零宽度模式类似于 (*PRUNE)
,但不同之处在于,在失败时,它还表示导致执行 (*SKIP)
模式的任何匹配文本都不能成为此模式的任何匹配的一部分。这实际上意味着,如果失败,正则表达式引擎将“跳过”到此位置并尝试再次匹配(假设有足够的匹配空间)。
(*SKIP:NAME)
模式的名称具有特殊意义。如果在匹配时遇到 (*MARK:NAME)
,则该位置将用作“跳过点”。如果未遇到该名称的 (*MARK)
,则 (*SKIP)
运算符不起作用。在不使用名称时,“跳过点”是执行 (*SKIP)
模式时的匹配点。
将以下内容与 (*PRUNE)
中的示例进行比较;请注意,字符串长度是两倍
'aaabaaab' =~ /a+b?(*SKIP)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";
输出
aaab
aaab
Count=2
一旦字符串开头的“aaab”匹配,并且执行了 (*SKIP)
,下一个起始点将是执行 (*SKIP)
时的光标所在位置。
(*MARK:NAME)
(*:NAME)
此零宽度模式可用于标记在成功匹配模式的某个部分时字符串中到达的点。可以给此标记一个名称。如果回溯到失败,稍后的 (*SKIP)
模式将跳到该点。允许使用任意数量的 (*MARK)
模式,并且可以重复使用NAME 部分。
除了与 (*SKIP)
模式交互之外,(*MARK:NAME)
还可用于“标记”模式分支,以便在匹配后,程序可以确定模式的哪些分支参与了匹配。
当匹配成功时,$REGMARK
变量将设置为参与匹配的最近执行的 (*MARK:NAME)
的名称。
这可用于确定匹配了模式的哪个分支,而无需为每个分支使用单独的捕获组,进而可以提高性能,因为 perl 无法像 /(?:x(*MARK:x)|y(*MARK:y)|z(*MARK:z))/
那样高效地优化 /(?:(x)|(y)|(z))/
。
当匹配失败时,除非另一个动词参与了匹配失败并提供了自己的名称,否则 $REGERROR
变量将设置为最近执行的 (*MARK:NAME)
的名称。
有关更多详细信息,请参阅 "(*SKIP)"。
作为快捷方式,(*MARK:NAME)
可以写成 (*:NAME)
。
(*THEN)
(*THEN:NAME)
这类似于 Raku 中的“cut group”运算符 ::
。与 (*PRUNE)
一样,此谓词始终匹配,并且当在失败时回溯时,它会导致正则表达式引擎尝试最内层封闭组(捕获或其他)中的下一个交替(如果存在交替)。就 (*THEN)
而言,(?(condition)yes-pattern|no-pattern)
的两个分支不算作交替。
它的名称来自这样的观察:此操作与交替运算符 ("|"
) 结合使用可以用来创建本质上基于模式的 if/then/else 块
( COND (*THEN) FOO | COND2 (*THEN) BAR | COND3 (*THEN) BAZ )
请注意,如果此运算符被用于交替之外,则它的作用与 (*PRUNE)
运算符完全相同。
/ A (*PRUNE) B /
与
/ A (*THEN) B /
相同,但
/ ( A (*THEN) B | C ) /
与
/ ( A (*PRUNE) B | C ) /
不同,因为在匹配 A 但在 B 上失败后,(*THEN)
谓词将回溯并尝试 C;但 (*PRUNE)
谓词将简单地失败。
(*COMMIT)
(*COMMIT:arg)
这是 Raku 的“commit pattern”<commit>
或 :::
。它是一个类似于 (*SKIP)
的零宽度模式,不同之处在于当在失败时回溯时,它会导致匹配直接失败。不会再进行任何通过推进起始指针来查找有效匹配的尝试。例如,
'aaabaaab' =~ /a+b?(*COMMIT)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n";
输出
aaab
Count=1
换句话说,一旦进入 (*COMMIT)
,并且如果模式不匹配,正则表达式引擎将不会再尝试在字符串的其余部分进行任何进一步的匹配。
(*FAIL)
(*F)
(*FAIL:arg)
此模式不匹配任何内容,并且始终失败。它可用于强制引擎回溯。它等效于 (?!)
,但更容易阅读。事实上,(?!)
在内部被优化为 (*FAIL)
。你可以提供一个参数,以便如果匹配因 FAIL
指令而失败,则可以从 $REGERROR
中获取该参数。
它可能仅在与 (?{})
或 (??{})
结合使用时才有用。
(*ACCEPT)
(*ACCEPT:arg)
此模式没有任何匹配项,并且无论字符串中是否还有更多匹配项,都会在遇到 (*ACCEPT)
模式时导致成功匹配结束。当位于嵌套模式(例如递归)或通过 (??{})
动态生成的子模式中时,只有最内层模式会立即结束。
如果 (*ACCEPT)
位于捕获组中,则这些组将被标记为在遇到 (*ACCEPT)
时结束。例如
'AB' =~ /(A (A|B(*ACCEPT)|C) D)(E)/x;
将匹配,并且 $1
将为 AB
,$2
将为 "B"
,$3
将不会设置。如果匹配了内部括号中的另一个分支,例如在字符串“ACDE”中,则还必须匹配 "D"
和 "E"
。
你可以提供一个参数,它将在匹配完成后在变量 $REGMARK
中可用。
\1
而不是 $1
的警告有些人过于习惯于编写类似这样的内容
$pattern =~ s/(\W)/\\\1/g;
对于替换的 RHS,这是为了避免让 sed 爱好者感到震惊而保留的(对于 \1 到 \9),但这是一个不好的习惯。这是因为在 PerlThink 中,s///
的右侧是一个双引号字符串。在通常的双引号字符串中,\1
表示控制-A。\1
的惯用 Unix 含义是为 s///
拼凑的。但是,如果你养成了这种习惯,那么如果你随后添加一个 /e
修饰符,你就会遇到麻烦。
s/(\d+)/ \1 + 1 /eg; # causes warning under -w
或者如果你尝试这样做
s/(\d+)/\1000/;
你无法通过说 \{1}000
来消除歧义,而你可以通过 ${1}000
来解决它。插值操作不应与匹配反向引用的操作混淆。当然,它们在 s///
的左侧表示两个不同的含义。
警告:前面有困难的材料(和散文)。本节需要重写。
正则表达式提供了一种简洁而强大的编程语言。与大多数其他动力工具一样,动力与造成破坏的能力相结合。
这种能力的常见滥用源于使用正则表达式进行无限循环的能力,例如像
'foo' =~ m{ ( o? )* }x;
o?
匹配“foo
”的开头,并且由于匹配不会移动字符串中的位置,因此由于 "*"
量词,o?
将一次又一次地匹配。创建类似循环的另一种常见方法是使用循环修饰符 /g
@matches = ( 'foo' =~ m{ o? }xg );
或
print "match: <$&>\n" while 'foo' =~ m{ o? }xg;
或由 split()
暗示的循环。
然而,长期的经验表明,通过使用可能匹配零长度子字符串的重复子表达式,可以极大地简化许多编程任务。这是一个简单的示例
@chars = split //, $string; # // is not magic in split
($whitewashed = $string) =~ s/()/ /g; # parens avoid magic s// /
因此,Perl 允许此类构造,通过强制中断无限循环。对于贪婪量词 *+{}
给出的较低级别循环和对于 /g
修饰符或 split()
运算符等较高级别循环,这些规则是不同的。
当 Perl 检测到重复表达式匹配零长度子字符串时,较低级别的循环会中断(即,循环中断)。因此
m{ (?: NON_ZERO_LENGTH | ZERO_LENGTH )* }x;
等效于
m{ (?: NON_ZERO_LENGTH )* (?: ZERO_LENGTH )? }x;
例如,此程序
#!perl -l
"aaaaab" =~ /
(?:
a # non-zero
| # or
(?{print "hello"}) # print hello whenever this
# branch is tried
(?=(b)) # zero-width assertion
)* # any number of times
/x;
print $&;
print $1;
打印
hello
aaaaa
b
请注意,“hello” 只打印一次,因为当 Perl 看到最外层 (?:)*
的第六次迭代匹配零长度字符串时,它会停止 "*"
。
较高级别的循环在迭代之间保留一个附加状态:上次匹配是否为零长度。要中断循环,禁止零长度匹配之后的下一个匹配具有零长度。此禁止与回溯交互(请参阅 "Backtracking"),因此,如果最佳匹配为零长度,则选择次佳匹配。
例如
$_ = 'bar';
s/\w??/<$&>/g;
结果为 <><b><><a><><r><>
。在字符串的每个位置,非贪婪 ??
给出的最佳匹配是零长度匹配,而次佳匹配是 \w
匹配的内容。因此,零长度匹配与一个字符长的匹配交替出现。
类似地,对于重复的 m/()/g
,次佳匹配是字符串中再往后一个刻度的匹配。
匹配零长度的附加状态与匹配的字符串相关联,并且通过每次分配给 pos()
重置。在 split
期间,忽略上次匹配末尾的零长度匹配。
之前描述的每个基本正则表达式片段(例如 ab
或 \Z
)最多可以在输入字符串的给定位置匹配一个子字符串。然而,在典型正则表达式中,这些基本片段使用组合运算符 ST
、S|T
、S*
等组合成更复杂的模式(在这些示例中,"S"
和 "T"
是正则子表达式)。
此类组合可能包括备选方案,从而导致选择问题:如果我们针对 "abc"
匹配正则表达式 a|ab
,它将匹配子字符串 "a"
还是 "ab"
?描述实际匹配哪个子字符串的一种方法是回溯概念(请参阅 "Backtracking")。然而,此描述过于底层,并且会让你从特定实现的角度进行思考。
另一种描述从“更好”/“更差”的概念开始。所有可能由给定正则表达式匹配的子字符串都可以从“最佳”匹配排序到“最差”匹配,并且选择“最佳”匹配。这用“哪个被选择?”的问题代替了“哪些匹配更好,哪些更差?”的问题。
同样,对于基本片段,不存在此类问题,因为在给定位置最多可能有一个匹配。本节描述了组合运算符的更好/更差概念。在下面的描述中,"S"
和 "T"
是正则子表达式。
ST
考虑两个可能的匹配 AB
和 A'B'
,"A"
和 A'
是可以由 "S"
匹配的子字符串,"B"
和 B'
是可以由 "T"
匹配的子字符串。
如果 "A"
比 A'
更匹配 "S"
,那么 AB
比 A'B'
更匹配。
如果 "A"
和 A'
重合:如果 "B"
比 B'
更匹配 "T"
,那么 AB
比 AB'
更匹配。
S|T
当 "S"
可以匹配时,它比只有 "T"
可以匹配时更匹配。
两个 "S"
匹配的顺序与 "S"
相同。对于两个 "T"
匹配也是如此。
S{REPEAT_COUNT}
匹配为 SSS...S
(根据需要重复多次)。
S{min,max}
匹配为 S{max}|S{max-1}|...|S{min+1}|S{min}
。
S{min,max}?
匹配为 S{min}|S{min+1}|...|S{max-1}|S{max}
。
S?
, S*
, S+
分别与 S{0,1}
、S{0,BIG_NUMBER}
、S{1,BIG_NUMBER}
相同。
S??
, S*?
, S+?
分别与 S{0,1}?
、S{0,BIG_NUMBER}?
、S{1,BIG_NUMBER}?
相同。
(?>S)
匹配 "S"
的最佳匹配,且仅匹配该最佳匹配。
(?=S)
, (?<=S)
仅考虑 "S"
的最佳匹配。(仅当 "S"
具有捕获括号,且在整个正则表达式中的其他位置使用反向引用时,这一点才很重要。)
(?!S)
, (?<!S)
对于此分组运算符,无需描述顺序,因为仅 "S"
是否可以匹配很重要。
(??{ EXPR })
, (?PARNO)
顺序与正则表达式相同,该正则表达式是 EXPR 的结果,或捕获组 PARNO 包含的模式。
(?(condition)yes-pattern|no-pattern)
请记住,实际上匹配 yes-pattern 还是 no-pattern 已确定。匹配的顺序与所选子表达式相同。
上述食谱描述了在特定位置匹配的顺序。还需要一个规则来了解如何确定整个正则表达式的匹配:在较早位置的匹配始终优于在较晚位置的匹配。
自 Perl 5.10.0 起,可以创建自定义正则表达式引擎。这并不适合胆小的人,因为他们必须在 C 级插入。有关更多详细信息,请参阅 perlreapi。
作为替代方案,重载常量(请参阅 overload)提供了一种简单的方法来扩展 RE 引擎的功能,方法是将一个模式替换为另一个模式。
假设我们要启用一个新的 RE 转义序列 \Y|
,它匹配空格字符和非空格字符之间的边界。请注意,(?=\S)(?<!\S)|(?!\S)(?<=\S)
在这些位置完全匹配,因此我们希望每个 \Y|
位于更复杂版本的所在位置。我们可以创建一个模块 customre
来执行此操作
package customre;
use overload;
sub import {
shift;
die "No argument to customre::import allowed" if @_;
overload::constant 'qr' => \&convert;
}
sub invalid { die "/$_[0]/: invalid escape '\\$_[1]'"}
# We must also take care of not escaping the legitimate \\Y|
# sequence, hence the presence of '\\' in the conversion rules.
my %rules = ( '\\' => '\\\\',
'Y|' => qr/(?=\S)(?<!\S)|(?!\S)(?<=\S)/ );
sub convert {
my $re = shift;
$re =~ s{
\\ ( \\ | Y . )
}
{ $rules{$1} or invalid($re,$1) }sgex;
return $re;
}
现在,use customre
在常量正则表达式(即不包含任何运行时变量插值的正则表达式)中启用新的转义。如 overload 中所述,此转换仅适用于正则表达式的文字部分。对于 \Y|$re\Y|
,此正则表达式的变量部分需要显式转换(但仅当 \Y|
的特殊含义应在 $re
中启用时才转换)
use customre;
$re = <>;
chomp $re;
$re = customre::convert $re;
/\Y|$re\Y|/;
(?{})
和 (??{})
在模式中执行的具体规则未指定,(*{})
的规则更加如此。在匹配成功的情况下,你可以假设它们会 DWIM,并且会在模式的接受路径中从左到右按适当的次数执行,就像任何其他元模式一样。非接受路径和匹配失败如何影响模式执行的次数是明确未指定的,并且可能会根据可以应用于模式的优化而有所不同,并且可能随着版本而改变。
例如,在
"aaabcdeeeee"=~/a(?{print "a"})b(?{print "b"})cde/;
中,在失败时打印出“a”或“b”的确切次数未指定,但你可以假设它们会在匹配成功期间至少打印一次,此外,你可以假设如果打印出“b”,它将至少由一个“a”开头。
对于像以下这样的分支结构
/a(b|(?{ print "a" }))c(?{ print "c" })/;
你可以假设输入“ac”将输出“ac”,而“abc”将仅输出“c”。
当嵌入代码被量化时,成功的匹配将为量词的每次匹配迭代调用代码一次。例如
"good" =~ /g(?:o(?{print "o"}))*d/;
将输出“o”两次。
出于历史和一致性的原因,在模式中的任何位置使用普通代码块都会禁用某些优化。从 5.37.7 开始,你可以使用“乐观”代码块 (*{ ... })
来替换 (?{ ... })
,如果你 *不* 希望禁用这些优化。这可能会导致代码块被调用的次数少于没有乐观的情况下。
从 Perl 5.10.0 开始,Perl 支持正则表达式语法中几个 Python/PCRE 特定的扩展。虽然鼓励 Perl 程序员使用 Perl 特定的语法,但以下内容也被接受
(?P<NAME>pattern)
定义一个命名的捕获组。等效于 (?<NAME>pattern)
。
(?P=NAME)
对命名的捕获组进行反向引用。等效于 \g{NAME}
。
(?P>NAME)
对命名的捕获组进行子例程调用。等效于 (?&NAME)
。
对于 Unicode 规则中的不区分大小写的匹配,存在许多问题。请参阅上面“修饰符”下的“i”
。
本文档从难以理解到完全不透明,变化不定。到处都是充斥着术语的冗长文字,难以理解。
本文档需要重写,将教程内容与参考内容分开。
Perl 模式匹配中使用的模式语法演变自贝尔实验室 Research Unix 第 8 版(版本 8)regex 例程中提供的语法。(该代码实际上(间接地)源自 Henry Spencer 对这些 V8 例程的自由再实现。)
Jeffrey Friedl 著,O'Reilly and Associates 出版的《掌握正则表达式》。