mirror of
https://github.com/php/doc-zh.git
synced 2026-03-24 15:12:20 +01:00
2104 lines
85 KiB
XML
2104 lines
85 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<!-- $Revision$ -->
|
||
<!-- EN-Revision: a0434e05111acabf2b9b2c7847e7e733f8dab0dc Maintainer: daijie Status: ready -->
|
||
<!-- CREDITS: mowangjuanzi, Luffy -->
|
||
<!-- splitted from ./en/functions/pcre.xml, last change in rev 1.2 -->
|
||
<chapter xml:id="reference.pcre.pattern.syntax" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||
<title>模式语法</title>
|
||
<titleabbrev>PCRE 正则语法</titleabbrev>
|
||
|
||
<section xml:id="regexp.introduction">
|
||
<title>简介</title>
|
||
<para>
|
||
本节描述的是 PCRE 支持的正则表达式语法和语义。正则表达式在 perl 的文档和另外一些书籍中也有讨论, 其中一些会有丰富的示例。O'Reilly(ISBN
|
||
1-56592-257-3)出版的 Jeffrey Friedl 的《精通正则表达式》一书非常详细的讨论了这些内容。这里的描述仅作为一个参考手册。
|
||
</para>
|
||
<para>
|
||
正则表达式是一个从左到右匹配目标字符串的模式。大多数字符自身就代表一个匹配
|
||
它们自身的模式。 作为一个简单的例子,模式 <literal>The quick brown fox</literal>
|
||
匹配目标字符串中与其相同的部分。
|
||
</para>
|
||
</section>
|
||
<section xml:id="regexp.reference.delimiters">
|
||
<title>分隔符</title>
|
||
<para>
|
||
当使用 PCRE 函数的时候,模式需要由<emphasis>分隔符</emphasis>闭合包裹。
|
||
分隔符可以是任意非字母数字、非反斜线、非空白字符。
|
||
静默忽略合法分隔符之前的空白字符。
|
||
</para>
|
||
<para>
|
||
经常使用的分隔符是正斜线(<literal>/</literal>)、hash符号(<literal>#</literal>)
|
||
以及取反符号(<literal>~</literal>)。下面的例子都是使用合法分隔符的模式。
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/foo bar/
|
||
#^[^0-9]$#
|
||
+php+
|
||
%[a-zA-Z0-9_-]%
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
</para>
|
||
<para>
|
||
It is also possible to use
|
||
bracket style delimiters where the opening and closing brackets are the
|
||
starting and ending delimiter, respectively. <literal>()</literal>,
|
||
<literal>{}</literal>, <literal>[]</literal> and <literal><></literal>
|
||
are all valid bracket style delimiter pairs.
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[
|
||
(this [is] a (pattern))
|
||
{this [is] a (pattern)}
|
||
[this [is] a (pattern)]
|
||
<this [is] a (pattern)>
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
Bracket style delimiters do not need to be escaped when they are used as meta
|
||
characters within the pattern, but as with other delimiters they must be
|
||
escaped when they are used as literal characters.
|
||
</para>
|
||
<para>
|
||
如果需要在正则模式内匹配分隔符,必须使用反斜线转义。如果分隔符经常在
|
||
正则模式内出现, 最好使用其他分隔符以便提高可读性。
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/http:\/\//
|
||
#http://#
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
需要将一个字符串放入模式中使用时,可以用 <function>preg_quote</function> 函数对其进行
|
||
转义,它的第二个参数(可选)可以用于指定需要被转义的分隔符。
|
||
</para>
|
||
<para>
|
||
可以在结束分隔符后面增加<link linkend="reference.pcre.pattern.modifiers">模式修饰符</link>。
|
||
下面的例子是一个大小写不敏感的匹配:
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[
|
||
#[a-z]#i
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
</para>
|
||
</section>
|
||
<section xml:id="regexp.reference.meta">
|
||
<title>元字符</title>
|
||
<para>
|
||
正则表达式的魔力源于它可以在模式中拥有选择和重复的能力。 一些字符被赋予
|
||
特殊的涵义,使其不再单纯的代表自己,模式中的这种有特殊涵义的编码字符
|
||
称为 <emphasis>元字符</emphasis>。
|
||
</para>
|
||
<para>
|
||
共有两种不同的元字符:一种是可以在模式中方括号外任何地方使用的,另外一种
|
||
是需要在方括号内使用的。
|
||
在方括号外使用的元字符如下:
|
||
|
||
|
||
<table>
|
||
<title>方括号外的元字符</title>
|
||
<tgroup cols="2">
|
||
<thead>
|
||
<row>
|
||
<entry>元字符</entry><entry>描述</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>\</entry><entry>一般用于转义字符</entry>
|
||
</row>
|
||
<row>
|
||
<entry>^</entry><entry>断言目标的开始位置(或在多行模式下是行首)</entry>
|
||
</row>
|
||
<row>
|
||
<entry>$</entry><entry>断言目标的结束位置(或在多行模式下是行尾)</entry>
|
||
</row>
|
||
<row>
|
||
<entry>.</entry><entry>匹配除换行符外的任何字符(默认)</entry>
|
||
</row>
|
||
<row>
|
||
<entry>[</entry><entry>开始字符类定义</entry>
|
||
</row>
|
||
<row>
|
||
<entry>]</entry><entry>结束字符类定义</entry>
|
||
</row>
|
||
<row>
|
||
<entry>|</entry><entry>开始一个可选分支</entry>
|
||
</row>
|
||
<row>
|
||
<entry>(</entry><entry>子组的开始标记</entry>
|
||
</row>
|
||
<row>
|
||
<entry>)</entry><entry>子组的结束标记</entry>
|
||
</row>
|
||
<row>
|
||
<entry>?</entry><entry>作为量词,表示 0 次或 1 次匹配。位于量词后面用于改变量词的贪婪特性。
|
||
(查阅<link linkend="regexp.reference.repetition">量词</link>)
|
||
</entry>
|
||
</row>
|
||
<row>
|
||
<entry>*</entry><entry>量词,0 次或多次匹配</entry>
|
||
</row>
|
||
<row>
|
||
<entry>+</entry><entry>量词,1 次或多次匹配</entry>
|
||
</row>
|
||
<row>
|
||
<entry>{</entry><entry>自定义量词开始标记</entry>
|
||
</row>
|
||
<row>
|
||
<entry>}</entry><entry>自定义量词结束标记</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
模式中方括号内的部分称为“<link linkend="regexp.reference.character-classes">字符类</link>”。
|
||
在一个字符类中仅有以下可用元字符:
|
||
|
||
<table>
|
||
<title>方括号内的元字符(<emphasis>字符类</emphasis>)</title>
|
||
<tgroup cols="2">
|
||
<thead>
|
||
<row>
|
||
<entry>元字符</entry><entry>描述</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>\</entry><entry>转义字符</entry>
|
||
</row>
|
||
<row>
|
||
<entry>^</entry><entry>仅在作为第一个字符(方括号内)时,表明字符类取反</entry>
|
||
</row>
|
||
<row>
|
||
<entry>-</entry><entry>标记字符范围</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
|
||
下面章节描述了每个元字符的用法。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.escape">
|
||
<title>转义序列(反斜线)</title>
|
||
<para>
|
||
反斜线有多种用法。首先,如果紧接着是一个非字母数字字符,表明取消
|
||
该字符所代表的特殊涵义。这种将反斜线作为转义字符的用法在字符类
|
||
内部和外部都可用。
|
||
</para>
|
||
<para>
|
||
比如,如果你希望匹配一个 "*" 字符,就需要在模式中写为 "\*"。
|
||
这适用于一个字符在不进行转义会有特殊含义的情况下。 但是,
|
||
对于非数字字母的字符,总是在需要其进行原文匹配的时候在它前面增加一个反斜线,
|
||
来声明它代表自己,这是安全的。如果要匹配一个反斜线,那么在模式中使用 ”\\”。
|
||
</para>
|
||
<note>
|
||
<para>
|
||
反斜线在单引号和双引号 PHP <link
|
||
linkend="language.types.string.syntax">字符串</link>
|
||
中都有特殊含义。因此要匹配一个反斜线 \,正则表达式写法是 \\,
|
||
然后 PHP 代码中需要转义写成 "\\\\" 或 '\\\\'。
|
||
</para>
|
||
</note>
|
||
<para>
|
||
如果一个模式被使用 <link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link> 选项编译,
|
||
模式中的空白字符(除了字符类中的)和未转义的#到行末的所有字符都会被忽略。
|
||
要在这种情况下使用空白字符或者#,就需要对其进行转义。
|
||
</para>
|
||
<para>
|
||
反斜线的第二种用途提供了一种对非打印字符进行可见编码的控制手段。
|
||
除了二进制的 0 会终结一个模式外,并不会严格的限制非打印字符(自身)的出现,
|
||
但是当一个模式以文本编辑器的方式编辑准备的时候,
|
||
使用下面的转义序列相比使用二进制字符会更加容易:
|
||
</para>
|
||
<para>
|
||
<variablelist>
|
||
<varlistentry>
|
||
<term><emphasis>\a</emphasis></term>
|
||
<listitem>
|
||
<simpara>响铃字符(十六进制 07)</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\cx</emphasis></term>
|
||
<listitem>
|
||
<simpara>"control-x",x 是任意字符</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\e</emphasis></term>
|
||
<listitem>
|
||
<simpara>转义 (十六进制 1B)</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\f</emphasis></term>
|
||
<listitem>
|
||
<simpara>换页 (十六进制 0C)</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\n</emphasis></term>
|
||
<listitem>
|
||
<simpara>换行 (十六进制 0A)</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\p{xx}</emphasis></term>
|
||
<listitem>
|
||
<simpara>
|
||
一个符合 xx 属性的字符,详细查看<link linkend="regexp.reference.unicode">unicode properties</link> 属性
|
||
</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\P{xx}</emphasis></term>
|
||
<listitem>
|
||
<simpara>
|
||
一个不符合xx属性的字符,详细查看<link linkend="regexp.reference.unicode">unicode properties</link> 属性
|
||
</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\r</emphasis></term>
|
||
<listitem>
|
||
<simpara>回车 (十六进制 0D)</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\R</emphasis></term>
|
||
<listitem>
|
||
<simpara>换行符:能匹配 \n、\r、\r\n</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\t</emphasis></term>
|
||
<listitem>
|
||
<simpara>水平制表符 tab (十六进制 09)</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\xhh</emphasis></term>
|
||
<listitem>
|
||
<simpara>
|
||
hh 十六进制编码的字符
|
||
</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\ddd</emphasis></term>
|
||
<listitem>
|
||
<simpara>ddd八进制编码的字符,或者后向引用</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
</variablelist>
|
||
</para>
|
||
<para>
|
||
<literal>\cx</literal>的确切效果如下: 如果<literal>x</literal>是一个小写字母,它被转换为大写。接着,
|
||
将字符的第6位(十六进制 40,右数第一个位为第0位)取反。
|
||
比如<literal>\cz</literal>成为十六进制的1A,<literal>\c{</literal>成为十六进制3B,
|
||
<literal>\c;</literal>成为十六进制7B。
|
||
</para>
|
||
<para>
|
||
在”<literal>\x</literal>”后面,读取两个十六进制数(字母可以是大写或小写)。
|
||
在<emphasis>UTF-8模式</emphasis>,
|
||
“<literal>\x{…}</literal>”允许使用, 花括号内的内容是十六进制有效数字。
|
||
它将给出的十六进制数字解释为 UTF-8 字符代码。原来的十六进制转义序列,
|
||
<literal>\xhh</literal>,
|
||
匹配一个双字节的UTF-8字符,如果它的值大于127
|
||
</para>
|
||
<para>
|
||
在”<literal>\0</literal>”之后, 读取两个八进制数。所有情况下,如果数少于2个,则直接使用。
|
||
序列 ”<literal>\0\x\07</literal>” 指定了两个二进制 0 紧跟着一个 BEL 字符。
|
||
请确保初始的 0 之后的两个数字是合法的八进制数。
|
||
</para>
|
||
<para>
|
||
处理一个反斜线紧跟着的不是0的数字的情况比较复杂。在字符类外部,
|
||
PCRE 读取它并以十进制读取紧随其后的数字。 如果数值小于 10,
|
||
或者之前捕获到了该数字能够代表的左括号(子组),
|
||
整个数字序列被认为是<emphasis>后向引用</emphasis>。后向引用如何工作在后面描述,
|
||
接下来就会讨论括号子组。
|
||
</para>
|
||
<para>
|
||
在一个字符类里面,或者十进制数大于 9 并且没有那么多的子组被捕获,
|
||
PCRE 重新读取反斜线后的第三个 8 进制数字,并且从最低的 8 位生成单字节值。
|
||
任何的后续数字都代表它们自身。例如:
|
||
</para>
|
||
<para>
|
||
<variablelist>
|
||
<varlistentry>
|
||
<term><emphasis>\040</emphasis></term>
|
||
<listitem><simpara>空格的另外一种用法</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\40</emphasis></term>
|
||
<listitem>
|
||
<simpara>
|
||
当提供了少于40个子组时也认为是空格。
|
||
</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\7</emphasis></term>
|
||
<listitem><simpara>始终是后向引用</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\11</emphasis></term>
|
||
<listitem>
|
||
<simpara>
|
||
可能是后向引用,也可能是制表符
|
||
</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\011</emphasis></term>
|
||
<listitem><simpara>总是一个制表符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\0113</emphasis></term>
|
||
<listitem><simpara>一个制表符紧跟着一个3(因为每次最多只读取3个8进制位</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\113</emphasis></term>
|
||
<listitem>
|
||
<simpara>
|
||
八进制113代表的字符(since there
|
||
can be no more than 99 back references)
|
||
</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\377</emphasis></term>
|
||
<listitem><simpara>8进制377是10进制255, 因此代表一个全1的字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\81</emphasis></term>
|
||
<listitem>
|
||
<simpara>
|
||
一个后向引用或者一个二进制 0 紧跟着两个数字 8 和 1(因为8不是8进制有效数字)
|
||
</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
</variablelist>
|
||
</para>
|
||
<para>
|
||
注意,八进制值的 100 或者更大的值必须没有前置的0引导,
|
||
因为每次最多读取3个8进制位.
|
||
</para>
|
||
<para>
|
||
所有序列定义的单字节值都可以在字符类内部或外部使用。另外,在字符类中,
|
||
序列 ”<literal>\b</literal>” 解释为退格字符(hex 08)。
|
||
字符类外它又有不同的意义(下面有描述)。
|
||
</para>
|
||
<para>
|
||
反斜线的第三种用法是用来描述特定的字符类:
|
||
</para>
|
||
<para>
|
||
<variablelist>
|
||
<varlistentry>
|
||
<term><emphasis>\d</emphasis></term>
|
||
<listitem><simpara>任意十进制数字</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\D</emphasis></term>
|
||
<listitem><simpara>任意非十进制数字</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\h</emphasis></term>
|
||
<listitem><simpara>任意水平空白字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\H</emphasis></term>
|
||
<listitem><simpara>任意非水平空白字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\s</emphasis></term>
|
||
<listitem><simpara>任意空白字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\S</emphasis></term>
|
||
<listitem><simpara>任意非空白字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\v</emphasis></term>
|
||
<listitem><simpara>任意垂直空白字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\V</emphasis></term>
|
||
<listitem><simpara>任意非垂直空白字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\w</emphasis></term>
|
||
<listitem><simpara>任意单词字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\W</emphasis></term>
|
||
<listitem><simpara>任意非单词字符</simpara></listitem>
|
||
</varlistentry>
|
||
</variablelist>
|
||
</para>
|
||
<para>
|
||
上面每一对转义序列都代表了完整字符集中两个不相交的部分,
|
||
任意字符一定会匹配其中一个,同时一定不会匹配另外一个。
|
||
</para>
|
||
<para>
|
||
"空白字符"(whitespace)是 HT (9)、LF (10)、FF (12)、CR (13)、space (32)。
|
||
然而,若发生了本地化匹配,在代码点 128-255 范围内亦可能出现空白字符,
|
||
比如说 NBSP (A0)。
|
||
</para>
|
||
<para>
|
||
单词字符指的是任意字母、数字、下划线。
|
||
也就是说任意可以组成perl<emphasis>单词</emphasis>的字符。
|
||
字母和数字的定义通过PCRE字符表控制,可以通过指定地域设置使其匹配改变。比如,
|
||
在法国 (fr) 本地化设置中,一些超过 128 的字符代码被用于重音字母,
|
||
它们可以实用 <literal>\w</literal> 匹配。
|
||
</para>
|
||
<para>
|
||
这些字符类序列在字符类内部或外部都可以出现。
|
||
他们每次匹配所代表的字符类型中的一个字符。 如果当前匹配点位于目标字符串末尾,
|
||
它们中的所有字符都匹配失败, 因为没有字符让它们匹配了。
|
||
</para>
|
||
<para>
|
||
反斜线的第四种用法是一些简单的断言。
|
||
一个断言指定一个必须在特定位置匹配的条件,
|
||
它们不会从目标字符串中消耗任何字符。
|
||
接下来我们会讨论使用子组的更加复杂的断言。 反斜线断言包括:
|
||
</para>
|
||
<para>
|
||
<variablelist>
|
||
<varlistentry>
|
||
<term><emphasis>\b</emphasis></term>
|
||
<listitem><simpara>单词边界</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\B</emphasis></term>
|
||
<listitem><simpara>非单词边界</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\A</emphasis></term>
|
||
<listitem><simpara>目标的开始位置(独立于多行模式)</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\Z</emphasis></term>
|
||
<listitem>
|
||
<simpara>
|
||
目标的结束位置或结束处的换行符(独立于多行模式)
|
||
</simpara>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\z</emphasis></term>
|
||
<listitem><simpara>目标的结束位置(独立于多行模式)</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\G</emphasis></term>
|
||
<listitem><simpara>在目标中首次匹配位置</simpara></listitem>
|
||
</varlistentry>
|
||
</variablelist>
|
||
</para>
|
||
<para>
|
||
这些断言不能出现在字符类中(但是注意, “<literal>\b</literal>”在字符类中有不同的意义,
|
||
表示的是退格(backspace)字符)
|
||
</para>
|
||
<para>
|
||
一个单词边界表示的是在目标字符串中,
|
||
当前字符和前一个字符不同时匹配<literal>\w</literal>或<literal>\W</literal>(一个匹配<literal>\w</literal>,
|
||
一个匹配<literal>\W</literal>),
|
||
或者作为字符串开始或结尾字符的时候当前字符匹配<literal>\w</literal>。
|
||
</para>
|
||
<para>
|
||
<literal>\A</literal>, <literal>\Z</literal>,
|
||
<literal>\z</literal>断言不同于传统的<literal>^</literal>和<literal>$</literal>(详见 <link linkend="regexp.reference.anchors">锚</link>),
|
||
因为他们永远匹配目标字符串的开始和结尾,而不会受模式修饰符的限制。
|
||
它们不受<link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>,<link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_DOLLAR_ENDONLY</link>选项的影响。
|
||
<literal>\Z</literal> 和 <literal>\z</literal> 之间的不同在于当字符串结束字符时换行符时 <literal>\Z</literal> 会将其看做字符串结尾匹配,
|
||
而 <literal>\z</literal> 只匹配字符串结尾。
|
||
</para>
|
||
<para>
|
||
<literal>\G</literal> 断言在指定了<parameter>offset</parameter> 参数的<function>preg_match</function> 调用中,
|
||
仅在当前匹配位置在匹配开始点的时候才是成功的。
|
||
当 <parameter>offset</parameter> 的值不为 0 的时候,
|
||
它与 <literal>\A</literal> 是不同的。 译注:另外一点与 <literal>\A</literal> 的不同之处在于使用 preg_match_all() 时,
|
||
每次匹配 <literal>\G</literal> 只是断言是否是匹配结果的开始位置,
|
||
而 <literal>\A</literal> 断言的则是匹配结果的开始位置是否在目标字符串开始位置。
|
||
</para>
|
||
|
||
<para>
|
||
<literal>\Q</literal> 和 <literal>\E</literal> 可以用于在模式中忽略正则表达式元字符。比如:<literal>\w+\Q.$.\E$</literal>
|
||
会匹配一个或多个单词字符,紧接着 <literal>.$.</literal>,最后锚向字符串末尾。注意这不会改变分隔符的行为;例如模式
|
||
<literal>#\Q#\E#$</literal> 无效,因为第二个 <literal>#</literal>
|
||
标记了模式的结束,而 <literal>\E#</literal> 解释为无效的修饰符。
|
||
</para>
|
||
|
||
<para>
|
||
<literal>\K</literal> 可以用于重置匹配。 比如,
|
||
<literal>foot\Kbar</literal> 匹配”footbar”。
|
||
但是得到的匹配结果是 ”bar”。但是, <literal>\K</literal> 的使用不会干预到子组内的内容,
|
||
比如 <literal>(foot)\Kbar</literal> 匹配 ”footbar”,第一个子组内的结果仍然会是 ”foo”。译注:
|
||
\K 放在子组和子组外面的效果是一样的。
|
||
</para>
|
||
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.unicode">
|
||
<title>Unicode 字符属性</title>
|
||
<para>
|
||
自 PHP 5.1.0 起,
|
||
三个额外的转义序列在选用 <emphasis>UTF-8 模式</emphasis>时用于匹配通用字符类型。他们是:
|
||
</para>
|
||
<variablelist>
|
||
<varlistentry>
|
||
<term><emphasis>\p{xx}</emphasis></term>
|
||
<listitem><simpara>一个有属性 xx 的字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\P{xx}</emphasis></term>
|
||
<listitem><simpara>一个没有属性 xx 的字符</simpara></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><emphasis>\X</emphasis></term>
|
||
<listitem><simpara>一个扩展的 Unicode 字符</simpara></listitem>
|
||
</varlistentry>
|
||
</variablelist>
|
||
<para>
|
||
上面 <literal>xx</literal> 代表的属性名用于限制 Unicode 通常的类别属性。
|
||
每个字符都有一个这样的确定的属性,通过两个缩写的字母指定。
|
||
为了与 Perl 兼容,
|
||
可以在左花括号 { 后面增加 ^ 表示取反。比如:
|
||
<literal>\p{^Lu}</literal> 就等同于 <literal>\P{Lu}</literal>。
|
||
</para>
|
||
<para>
|
||
如果通过 <literal>\p</literal> 或 <literal>\P</literal> 仅指定了一个字母,它包含所有以这个字母开头的属性。
|
||
在这种情况下,花括号的转义序列是可选的;以下两个例子是等同的:
|
||
</para>
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[
|
||
\p{L}
|
||
\pL
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
<table>
|
||
<title>支持的 Unicode 属性</title>
|
||
<tgroup cols="3">
|
||
<thead>
|
||
<row>
|
||
<entry>Property</entry>
|
||
<entry>Matches</entry>
|
||
<entry>Notes</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry><literal>C</literal></entry>
|
||
<entry>Other</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Cc</literal></entry>
|
||
<entry>Control</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Cf</literal></entry>
|
||
<entry>Format</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Cn</literal></entry>
|
||
<entry>Unassigned</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Co</literal></entry>
|
||
<entry>Private use</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row rowsep="1">
|
||
<entry><literal>Cs</literal></entry>
|
||
<entry>Surrogate</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>L</literal></entry>
|
||
<entry>Letter</entry>
|
||
<entry>
|
||
包含以下属性:<literal>Ll</literal>、
|
||
<literal>Lm</literal>、<literal>Lo</literal>、<literal>Lt</literal>、
|
||
<literal>Lu</literal>.
|
||
</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Ll</literal></entry>
|
||
<entry>小写字母</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Lm</literal></entry>
|
||
<entry>Modifier letter</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Lo</literal></entry>
|
||
<entry>Other letter</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Lt</literal></entry>
|
||
<entry>Title case letter</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row rowsep="1">
|
||
<entry><literal>Lu</literal></entry>
|
||
<entry>Upper case letter</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>M</literal></entry>
|
||
<entry>Mark</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Mc</literal></entry>
|
||
<entry>Spacing mark</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Me</literal></entry>
|
||
<entry>Enclosing mark</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row rowsep="1">
|
||
<entry><literal>Mn</literal></entry>
|
||
<entry>Non-spacing mark</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>N</literal></entry>
|
||
<entry>Number</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Nd</literal></entry>
|
||
<entry>Decimal number</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Nl</literal></entry>
|
||
<entry>Letter number</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row rowsep="1">
|
||
<entry><literal>No</literal></entry>
|
||
<entry>Other number</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>P</literal></entry>
|
||
<entry>Punctuation</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Pc</literal></entry>
|
||
<entry>Connector punctuation</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Pd</literal></entry>
|
||
<entry>Dash punctuation</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Pe</literal></entry>
|
||
<entry>Close punctuation</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Pf</literal></entry>
|
||
<entry>Final punctuation</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Pi</literal></entry>
|
||
<entry>Initial punctuation</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Po</literal></entry>
|
||
<entry>Other punctuation</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row rowsep="1">
|
||
<entry><literal>Ps</literal></entry>
|
||
<entry>Open punctuation</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>S</literal></entry>
|
||
<entry>Symbol</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Sc</literal></entry>
|
||
<entry>Currency symbol</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Sk</literal></entry>
|
||
<entry>Modifier symbol</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Sm</literal></entry>
|
||
<entry>Mathematical symbol</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row rowsep="1">
|
||
<entry><literal>So</literal></entry>
|
||
<entry>Other symbol</entry>
|
||
<entry>Includes emojis</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Z</literal></entry>
|
||
<entry>Separator</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Zl</literal></entry>
|
||
<entry>Line separator</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Zp</literal></entry>
|
||
<entry>Paragraph separator</entry>
|
||
<entry></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Zs</literal></entry>
|
||
<entry>Space separator</entry>
|
||
<entry></entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<para>
|
||
<literal>InMusicalSymbols</literal> 等扩展属性在 PCRE 中不支持
|
||
</para>
|
||
<para>
|
||
指定大小写不敏感匹配对这些转义序列不会产生影响,比如,
|
||
<literal>\p{Lu}</literal> 始终匹配大写字母。
|
||
</para>
|
||
<para>
|
||
Unicode 字符集在具体文字中定义。使用文字名可以匹配这些字符集中的一个字符。例如:
|
||
</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara><literal>\p{Greek}</literal></simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara><literal>\P{Han}</literal></simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>
|
||
不在确定文字中的则被集中到 <literal>Common</literal>。当前的文字列表中有:
|
||
</para>
|
||
<table>
|
||
<title>支持的文字</title>
|
||
<tgroup cols="5">
|
||
<tbody>
|
||
<row>
|
||
<entry><literal>Arabic</literal></entry>
|
||
<entry><literal>Armenian</literal></entry>
|
||
<entry><literal>Avestan</literal></entry>
|
||
<entry><literal>Balinese</literal></entry>
|
||
<entry><literal>Bamum</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Batak</literal></entry>
|
||
<entry><literal>Bengali</literal></entry>
|
||
<entry><literal>Bopomofo</literal></entry>
|
||
<entry><literal>Brahmi</literal></entry>
|
||
<entry><literal>Braille</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Buginese</literal></entry>
|
||
<entry><literal>Buhid</literal></entry>
|
||
<entry><literal>Canadian_Aboriginal</literal></entry>
|
||
<entry><literal>Carian</literal></entry>
|
||
<entry><literal>Chakma</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Cham</literal></entry>
|
||
<entry><literal>Cherokee</literal></entry>
|
||
<entry><literal>Common</literal></entry>
|
||
<entry><literal>Coptic</literal></entry>
|
||
<entry><literal>Cuneiform</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Cypriot</literal></entry>
|
||
<entry><literal>Cyrillic</literal></entry>
|
||
<entry><literal>Deseret</literal></entry>
|
||
<entry><literal>Devanagari</literal></entry>
|
||
<entry><literal>Egyptian_Hieroglyphs</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Ethiopic</literal></entry>
|
||
<entry><literal>Georgian</literal></entry>
|
||
<entry><literal>Glagolitic</literal></entry>
|
||
<entry><literal>Gothic</literal></entry>
|
||
<entry><literal>Greek</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Gujarati</literal></entry>
|
||
<entry><literal>Gurmukhi</literal></entry>
|
||
<entry><literal>Han</literal></entry>
|
||
<entry><literal>Hangul</literal></entry>
|
||
<entry><literal>Hanunoo</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Hebrew</literal></entry>
|
||
<entry><literal>Hiragana</literal></entry>
|
||
<entry><literal>Imperial_Aramaic</literal></entry>
|
||
<entry><literal>Inherited</literal></entry>
|
||
<entry><literal>Inscriptional_Pahlavi</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Inscriptional_Parthian</literal></entry>
|
||
<entry><literal>Javanese</literal></entry>
|
||
<entry><literal>Kaithi</literal></entry>
|
||
<entry><literal>Kannada</literal></entry>
|
||
<entry><literal>Katakana</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Kayah_Li</literal></entry>
|
||
<entry><literal>Kharoshthi</literal></entry>
|
||
<entry><literal>Khmer</literal></entry>
|
||
<entry><literal>Lao</literal></entry>
|
||
<entry><literal>Latin</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Lepcha</literal></entry>
|
||
<entry><literal>Limbu</literal></entry>
|
||
<entry><literal>Linear_B</literal></entry>
|
||
<entry><literal>Lisu</literal></entry>
|
||
<entry><literal>Lycian</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Lydian</literal></entry>
|
||
<entry><literal>Malayalam</literal></entry>
|
||
<entry><literal>Mandaic</literal></entry>
|
||
<entry><literal>Meetei_Mayek</literal></entry>
|
||
<entry><literal>Meroitic_Cursive</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Meroitic_Hieroglyphs</literal></entry>
|
||
<entry><literal>Miao</literal></entry>
|
||
<entry><literal>Mongolian</literal></entry>
|
||
<entry><literal>Myanmar</literal></entry>
|
||
<entry><literal>New_Tai_Lue</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Nko</literal></entry>
|
||
<entry><literal>Ogham</literal></entry>
|
||
<entry><literal>Old_Italic</literal></entry>
|
||
<entry><literal>Old_Persian</literal></entry>
|
||
<entry><literal>Old_South_Arabian</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Old_Turkic</literal></entry>
|
||
<entry><literal>Ol_Chiki</literal></entry>
|
||
<entry><literal>Oriya</literal></entry>
|
||
<entry><literal>Osmanya</literal></entry>
|
||
<entry><literal>Phags_Pa</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Phoenician</literal></entry>
|
||
<entry><literal>Rejang</literal></entry>
|
||
<entry><literal>Runic</literal></entry>
|
||
<entry><literal>Samaritan</literal></entry>
|
||
<entry><literal>Saurashtra</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Sharada</literal></entry>
|
||
<entry><literal>Shavian</literal></entry>
|
||
<entry><literal>Sinhala</literal></entry>
|
||
<entry><literal>Sora_Sompeng</literal></entry>
|
||
<entry><literal>Sundanese</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Syloti_Nagri</literal></entry>
|
||
<entry><literal>Syriac</literal></entry>
|
||
<entry><literal>Tagalog</literal></entry>
|
||
<entry><literal>Tagbanwa</literal></entry>
|
||
<entry><literal>Tai_Le</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Tai_Tham</literal></entry>
|
||
<entry><literal>Tai_Viet</literal></entry>
|
||
<entry><literal>Takri</literal></entry>
|
||
<entry><literal>Tamil</literal></entry>
|
||
<entry><literal>Telugu</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Thaana</literal></entry>
|
||
<entry><literal>Thai</literal></entry>
|
||
<entry><literal>Tibetan</literal></entry>
|
||
<entry><literal>Tifinagh</literal></entry>
|
||
<entry><literal>Ugaritic</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>Vai</literal></entry>
|
||
<entry><literal>Yi</literal></entry>
|
||
<entry />
|
||
<entry />
|
||
<entry />
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<para>
|
||
<literal>\X</literal> 转义匹配了 Unicode 可扩展字符集(Unicode extended grapheme clusters)。
|
||
可扩展字符集是一个或多个 Unicode 字符,组合表达了单个象形字符。
|
||
因此无论渲染时实际使用了多少个独立字符,可以视该 Unicode 等同于 <literal>.</literal>,
|
||
会匹配单个组合后的字符。
|
||
</para>
|
||
<para>
|
||
小于 PCRE 8.32 的版本中(对应小于 PHP 5.4.14 的内置绑定 PCRE 库),
|
||
<literal>\X</literal> 等价于 <literal>(?>\PM\pM*)</literal>。
|
||
也就是说,它匹配一个没有 ”mark” 属性的字符,紧接着任意多个由 ”mark” 属性的字符。
|
||
并将这个序列认为是一个原子组(详见下文)。
|
||
典型的有 ”mark” 属性的字符是影响到前面的字符的重音符。
|
||
</para>
|
||
<para>
|
||
用 Unicode 属性来匹配字符的速度并不快,
|
||
因为 PCRE 需要去搜索一个包含超过 15000 字符的数据结构。
|
||
这就是为什么在 PCRE中 要使用传统的转义序列<literal>\d</literal>、
|
||
<literal>\w</literal> 而不使用 Unicode 属性的原因。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.anchors">
|
||
<title>锚</title>
|
||
<para>
|
||
在一个字符类外面,在默认匹配模式下,
|
||
脱字符(<literal>^</literal>) 是一个断言当前匹配点位于目标字符串开始处的断言。
|
||
在一个字符类内部,
|
||
脱字符(<literal>^</literal>) 表明这个字符类中描述的字符取反(详见下文)。
|
||
</para>
|
||
<para>
|
||
脱字符(<literal>^</literal>)并不一定要是模式的第一个字符, 但是如果处于某个可选分支时,
|
||
它应该是该分支的首字符。如果所有选择分支都以脱字符(<literal>^</literal>)开头,这就是说,
|
||
如果模式限制为只匹配目标的开头,
|
||
它被称为是一个 ”紧固” 模式。(同样也有其他方式可以构造出紧固模式)
|
||
</para>
|
||
<para>
|
||
美元符(<literal>$</literal>)是用于断言当前匹配点位于目标字符串末尾,
|
||
或当目标字符串以换行符结尾时当前匹配点位于该换行符位置(默认情况)。
|
||
美元符(<literal>$</literal>)不一定要作为模式的最后一个字符,但是如果它在某个可选分支中时,
|
||
就应该位于该分支的末尾。美元符在字符类中没有特殊的意义。
|
||
</para>
|
||
<para>
|
||
<literal>$</literal> 的意义可以通过在编译或匹配时设置 <link linkend="reference.pcre.pattern.modifiers">PCRE_DOLLAR_ENDONLY</link> 改变为只匹配字符串末尾。
|
||
这不会影响 \Z 断言的行为。
|
||
</para>
|
||
<para>
|
||
脱字符 <literal>^</literal> 和美元符 <literal>$</literal> 字符的意义在
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link> 选项被设置时会发生变化。 当在这种情况下时,
|
||
它们匹配每一个换行符后面的和前面的字符,另外,
|
||
也会匹配目标字符串的开始和结束。比如,
|
||
模式 /^abc$/ 在多行模式下会成功匹配目标字符串 ”def\nabc”,
|
||
而正常情况下不会。因此,由于所有的可选分支都以 "^" 开始,
|
||
在单行模式下这成为紧固模式,然而在多行模式下,这是非紧固的。
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOLLAR_ENDONLY</link>
|
||
选项在 <link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>
|
||
设置后失效。
|
||
</para>
|
||
<para>
|
||
注意: \A、\Z、
|
||
\z 等转义序列可以在任何模式下用于匹配目标字符串的开始和结束位置。
|
||
并且如果模式的所有分支都以 \A 开始,它同样是紧固的,
|
||
而与 <link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>
|
||
是否设置无关。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.dot">
|
||
<title>句点</title>
|
||
<para>
|
||
在字符类外部,模式中的句点匹配目标字符串中的任意字符,包括非打印字符,
|
||
但是(默认)不包括换行符。如果设置了
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>,
|
||
句点就会匹配换行符。
|
||
句点的处理和^、$的处理没有关联,它们唯一的关系是它们都涉及到了换行符。
|
||
句点在字符类中没有任何意义。
|
||
</para>
|
||
<para>
|
||
<emphasis>\C</emphasis>可以被用于匹配单字节,
|
||
也就是说在<emphasis>UTF-8 模式</emphasis>下,句点可以匹配多字节字符。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.character-classes">
|
||
<title>字符类(方括号)</title>
|
||
<para>
|
||
左方括号开始一个字符类的描述,并以方中括号结束。
|
||
单独的一个右方括号没有特殊含义。如果一个右方括号需要作为一个字符类中的成员,
|
||
那么可以将它写在字符类的首字符处(如果使用了 ^ 取反,
|
||
那么是第二个)或者使用转义符。
|
||
</para>
|
||
<para>
|
||
一个字符类在目标字符串中匹配一个单独的字符;
|
||
该字符必须是字符类中定义的字符集合的其中一个, 除非使用了 ^ 对字符类取反。
|
||
如果^需要作为一个字符类的成员,确保它不是该字符类的首字符,
|
||
或者对其进行转义即可。
|
||
</para>
|
||
<para>
|
||
例如,字符类[aeiou]匹配所有的小写元音字母,
|
||
而[^aeiou]匹配所有非元音字母的字符。注意:
|
||
^只是一个通过枚举指定那些不存在字符类之中的字符的便利符号。而不是断言,
|
||
它仍然会从目标字符串中消耗一个字符,并且如果当前匹配点在目标字符串末尾,
|
||
匹配将会失败。
|
||
</para>
|
||
<para>
|
||
当大小写无关匹配被设置后,任意字符类都同时代表大小写两种版本,因此对于例子,
|
||
一个大小写不敏感的[aeiou]同时匹配"A"和"a",
|
||
并且大小写不敏感的[^aeiou]同时不匹配
|
||
"A"。
|
||
</para>
|
||
<para>
|
||
换行符在字符类中没有任何特殊涵义, 与 <link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link> 或
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link> 选项都无关。
|
||
例如 [^a] 这样的字符类始终会匹配换行符。
|
||
</para>
|
||
<para>
|
||
在字符类中,一个中划线(减号 -)可以用于指定从一个字符到另一个字符的范围。
|
||
比如,[d-m]匹配d到m之间的所有字符,这个集合时闭合的。
|
||
如果中划线自身要在一个字符类中描述,
|
||
它必须被转移或者出现在一个不会被解释为一个范围的位置,
|
||
典型的比如字符类开始或结束位置。
|
||
</para>
|
||
<para>
|
||
在一个字符范围描述后面不能使用右中括号。
|
||
比如一个模式 [W-]46] 被解释为一个包含 W 和 - 的字符类,后面紧跟字符串 ”46]”,
|
||
因此它可以匹配 ”W46]” 或 ”-46]”。然而, 如果中括号是经过转义的,
|
||
它将会被解释为范围的终点,
|
||
因此 [W-\]46] 就会被解释为一个单独的包含 W 至 ] 范围内所有字符以及 4、6 的字符类。
|
||
8 进制或 16 进制描述的中括号同样可以用于作为范围的终点。
|
||
</para>
|
||
<para>
|
||
范围操作以 ASCII 整理排序。它们可以用于为字符指定数值,比如 [\000-\037]。
|
||
如果在大小写不敏感匹配模式下使用一个包含字母的范围,
|
||
则同时匹配它的大小写形式。
|
||
比如 [W-c] 在不区分大小写匹配时等价于 [][\^_`wxyzabc],并且,
|
||
如果使用了 ”fr”(法国) 的地域设置字符表时,
|
||
[\xc8-xcb] 将会在所有模式下匹配重音 E 字符。
|
||
</para>
|
||
<para>
|
||
字符类\d、\D、 \s、\S、\w 和 \W 也可以出现在一个字符类中,
|
||
用以将其匹配的字符类加入到新的自定义字符类中。比如,
|
||
[\dABCDEF] 匹配任意合法的 16 进制数。用 ^ 可以很方便的制定严格的字符类,
|
||
比如 [^\W_] 匹配任何字母或数字,但不匹配下划线。
|
||
</para>
|
||
<para>
|
||
所有非字母数字字符除了\、-、
|
||
^(在起始位置)以及结束的]在字符类中都是非特殊字符,
|
||
没有转义也不会有危害。模式结束符在表达式中总是特殊字符,必须进行转义。
|
||
</para>
|
||
<para>
|
||
Perl 支持 POSIX 字符类符号。这种字符类使用<literal>[:</literal>和<literal>:]</literal>闭合。 PCRE 同样支持这些字符类,
|
||
比如,<literal>[01[:alpha:]%]</literal>匹配 ”0”、“1”、任意字母或”%”。 支持的字符类如下:
|
||
<table>
|
||
<title>字符类</title>
|
||
<tgroup cols="2">
|
||
<tbody>
|
||
<row><entry><literal>alnum</literal></entry><entry>字母和数字</entry></row>
|
||
<row><entry><literal>alpha</literal></entry><entry>字母</entry></row>
|
||
<row><entry><literal>ascii</literal></entry><entry>0 - 127的ascii字符</entry></row>
|
||
<row><entry><literal>blank</literal></entry><entry>空格和水平制表符</entry></row>
|
||
<row><entry><literal>cntrl</literal></entry><entry>控制字符</entry></row>
|
||
<row><entry><literal>digit</literal></entry><entry>十进制数(same as \d)</entry></row>
|
||
<row><entry><literal>graph</literal></entry><entry>打印字符, 不包括空格</entry></row>
|
||
<row><entry><literal>lower</literal></entry><entry>小写字母</entry></row>
|
||
<row><entry><literal>print</literal></entry><entry>打印字符,包含空格</entry></row>
|
||
<row><entry><literal>punct</literal></entry><entry>打印字符, 不包括字母和数字</entry></row>
|
||
<row><entry><literal>space</literal></entry><entry>空白字符 (比\s多垂直制表符)</entry></row>
|
||
<row><entry><literal>upper</literal></entry><entry>大写字母</entry></row>
|
||
<row><entry><literal>word</literal></entry><entry>单词字符(和 \w 一样)</entry></row>
|
||
<row><entry><literal>xdigit</literal></entry><entry>十六进制数字</entry></row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
空白字符有HT(9)、 LF(10)、VT(11)、 FF(12)、CR(13)、space(32)。 注意,
|
||
这个列表包含了垂直制表符。这使得space不同于<literal>\s</literal>,
|
||
因为它不包含垂直制表符(为了向 Perl 兼容)
|
||
</para>
|
||
<para>
|
||
<literal>[:word:]</literal>是一个 Perl扩展,<literal>[:blank:]</literal>是一个从 Perl5.8 中来的 GNU 扩展。
|
||
另外一个 Perl 扩展是取反,通过前置一个<literal>^</literal>。 比如,
|
||
<literal>[12[:^digit:]]</literal> 匹配”1”,
|
||
“2” 或任何非数字字符
|
||
</para>
|
||
<para>
|
||
在 UTF-8 模式中,大于 128 的字符值不会匹配任何 POSIX 字符类。
|
||
自 libpcre 8.10 起,某些字符类改为使用 Unicode 字符属性,所以不会出现提醒中的限制。
|
||
详情参阅 <link xlink:href="&url.pcre.man;">PCRE(3) manual</link>。
|
||
</para>
|
||
<para>
|
||
Unicode character properties can appear inside a character class. They can
|
||
not be part of a range. The minus (hyphen) character after a Unicode
|
||
character class will match literally. Trying to end a range with a Unicode
|
||
character property will result in a warning.
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.alternation">
|
||
<title>可选路径(|)</title>
|
||
<para>
|
||
竖线字符用于分离模式中的可选路径。
|
||
比如模式<literal>gilbert|Sullivan</literal>匹配 ”gilbert” 或者 ”sullivan”。
|
||
竖线可以在模式中出现任意多个,并且允许有空的可选路径(匹配空字符串)。
|
||
匹配的处理从左到右尝试每一个可选路径,并且使用第一个成功匹配的。
|
||
如果可选路径在子组(下面定义)中,
|
||
则”成功匹配”表示同时匹配了子模式中的分支以及主模式中的其他部分。
|
||
</para>
|
||
<para>
|
||
可以通过使用 <literal>(*MARK:NAME)</literal> 或 <literal>(*:NAME)</literal> 记录匹配到的是哪个可选分支。
|
||
可以出现任意数量的 <literal>(*MARK)</literal> 动词,并且它们的名称不必唯一。
|
||
当匹配成功时,最后遇到的 <literal>(*MARK:NAME)</literal> 的名称会作为名为
|
||
<literal>MARK</literal> 的捕获组出现在匹配结果中,因此可以从
|
||
<function>preg_match</function> 的 <parameter>matches</parameter> 读取,
|
||
也会传递给 <function>preg_replace_callback</function> 的 <parameter>callback</parameter> 等。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.internal-options">
|
||
<title>内部选项设置</title>
|
||
<para>
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_CASELESS</link>、
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>、
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>、
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_UNGREEDY</link>、
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTRA</link>、
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>、
|
||
PCRE_DUPNAMES 等模式修饰符设置可以在模式内部通过一个 Perl 选项字符序列来设置,
|
||
语法是 (?修饰符),可用的修饰符有:
|
||
|
||
<table>
|
||
<title>Internal option letters</title>
|
||
<tgroup cols="2">
|
||
<tbody>
|
||
<row>
|
||
<entry><literal>i</literal></entry>
|
||
<entry>for <link linkend="reference.pcre.pattern.modifiers">PCRE_CASELESS</link></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>m</literal></entry>
|
||
<entry>for <link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>s</literal></entry>
|
||
<entry>for <link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>x</literal></entry>
|
||
<entry>for <link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>U</literal></entry>
|
||
<entry>for <link linkend="reference.pcre.pattern.modifiers">PCRE_UNGREEDY</link></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>X</literal></entry>
|
||
<entry>for <link linkend="reference.pcre.pattern.modifiers">PCRE_EXTRA</link></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>J</literal></entry>
|
||
<entry>for <link linkend="reference.pcre.pattern.modifiers">PCRE_INFO_JCHANGED</link></entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
</para>
|
||
<para>
|
||
比如,(?im) 设置表明多行大小写不敏感匹配。同样可以用它来取消这些设置,
|
||
比如 (?im-sx) 设置了 <link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_CASELESS</link>,<link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>,
|
||
但是同时取消了 <link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link> 和 <link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>。 如果一个字母即出现在 - 之前,
|
||
也出现在 - 之后,这个选项被取消设置。
|
||
</para>
|
||
<para>
|
||
当一个选项在模式的最上级(也就是说不在子组中)时,
|
||
这个改变会影响模式中剩余部分。比如 <literal>/ab(?i)c/</literal> 仅仅匹配 ”abc” 和 ”abC”。
|
||
</para>
|
||
<para>
|
||
如果一个选项在子组中设置,产生的影响是不同的。这是 Perl 5.005 中行为的一个变种。
|
||
一个选项在子组内部设置,仅仅改变子组中剩余的部分,
|
||
因此 <literal>(a(?i)b)c</literal> 仅仅匹配 ”abc” 和 ”aBc” (假设没有使用
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_CASELESS</link> 选项)。
|
||
这就意味着选项在模式的不同位置可以造成不同的影响。
|
||
在同一个子模式中,
|
||
一个分支的选项设置回穿透到后面剩余的其他分支中去。
|
||
比如 <literal>(a(?i)b|c)</literal> 匹配”ab”,
|
||
“aB”, “c” 和 ”C”。 尽管在匹配 ”C” 时第一个分支会在选项被设定前就被丢弃。
|
||
这是因为选项的设定是在编译期确定的,否则可能会带来非常怪异的行为。
|
||
</para>
|
||
<para>
|
||
PCRE 专用选项 <link linkend="reference.pcre.pattern.modifiers">PCRE_UNGREEDY</link>
|
||
和 <link linkend="reference.pcre.pattern.modifiers">PCRE_EXTRA</link>
|
||
可以和 Perl 兼容选项以同样的方式来改变,
|
||
分别使用字母 U 和 X。 (?X) 标记设定有些特殊,它必须出现在任何其他特性之前,
|
||
最好放在最开头的位置。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.subpatterns">
|
||
<title>子组(子模式)</title>
|
||
<para>
|
||
子组通过圆括号分隔界定,并且它们可以嵌套。
|
||
将一个模式中的一部分标记为子组(子模式)主要是来做两件事情:
|
||
</para>
|
||
<orderedlist>
|
||
<listitem>
|
||
<para>
|
||
将可选分支局部化。比如,模式<literal>cat(arcat|erpillar|)</literal>匹配 ”cat”, “cataract”,
|
||
“caterpillar” 中的一个,如果没有圆括号的话,它匹配的则是 ”cataract”,
|
||
“erpillar” 以及空字符串。
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
将子组设定为捕获子组(向上面定义的)。当整个模式匹配后,
|
||
目标字符串中匹配子组的部分将会通过 <function>pcre_exec()</function>
|
||
的 <emphasis>ovector</emphasis> 参数回传给调用者。
|
||
左括号从左至右出现的次序就是对应子组的下标(从 1 开始),
|
||
可以通过这些下标数字来获取捕获子模式匹配结果。
|
||
</para>
|
||
</listitem>
|
||
</orderedlist>
|
||
<para>
|
||
比如,如果字符串 ”the red king” 使用模式<literal>((red|white)
|
||
(king|queen))</literal> 进行匹配,
|
||
模式匹配到的结果是 array(“red king”, ”red king”, “red”, “king”) 的形式,
|
||
其中第 0 个元素是整个模式匹配的结果,后面的三个元素依次为三个子组匹配的结果。
|
||
它们的下标分别为 1, 2, 3。
|
||
</para>
|
||
<para>
|
||
事实上,并不一定同时需要圆括号的两种功能。
|
||
经常我们会需要子组进行分组, 但又不需要(单独的)捕获它们。
|
||
在子组定义的左括号后面紧跟字符串 ”?:” 会使得该子组不被单独捕获,
|
||
并且不会对其后子组序号的计算产生影响。比如, 如果字符串 "the white queen"
|
||
匹配模式 <literal>the ((?:red|white) (king|queen))</literal>,
|
||
匹配到的子串是 "white queen" 和 "queen",
|
||
他们的下标分别是 1 和 2。
|
||
捕获子组的最大序号为 65535。然而由于 libpcre 的配置,我们可能无法编译这么长的
|
||
正则表达式。
|
||
</para>
|
||
<para>
|
||
为了方便简写,如果需要在非捕获子组开始位置设置选项,
|
||
选项字母可以位于 ? 和 : 之间,比如两个模式:
|
||
</para>
|
||
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[
|
||
(?i:saturday|sunday)
|
||
(?:(?i)saturday|sunday)
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
|
||
<para>
|
||
匹配到了完全相同的字符集。因为可选分支会从左到右尝试每个分支,
|
||
并且选项没有在子模式结束前被重置,
|
||
并且由于选项的设置会穿透对后面的其他分支产生影响,因此,
|
||
上面的模式都会匹配 ”SUNDAY” 以及 ”Saturday”。
|
||
</para>
|
||
|
||
<para>
|
||
可以对子组使用 <literal>(?P<name>pattern)</literal> 的语法进行命名。
|
||
这个子模式将会在匹配结果中同时以其名称和顺序(数字下标)出现,
|
||
还有两种为子组命名的语法:
|
||
<literal>(?<name>pattern)</literal> 和 <literal>(?'name'pattern)</literal>。
|
||
</para>
|
||
|
||
<para>
|
||
有时需要多个匹配可以在一个正则表达式中选用子组。
|
||
为了让多个子组可以共用一个后向引用数字的问题,
|
||
<literal>(?|</literal> 语法允许复制数字。
|
||
考虑下面的正则表达式匹配<literal>Sunday</literal>:
|
||
</para>
|
||
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[(?:(Sat)ur|(Sun))day]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
|
||
<para>
|
||
<literal>Sun</literal> 存储在反向引用 2 中,而反向引用 1 为空。匹配
|
||
<literal>Saturday</literal> 则 <literal>Sat</literal>
|
||
存储在反向引用 1 中,而反向引用 2 不存在。使用 <literal>(?|</literal> 修改模式来修复这个问题:
|
||
</para>
|
||
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[(?|(Sat)ur|(Sun))day]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
|
||
<para>
|
||
使用这个模式,
|
||
<literal>Sun</literal> 和 <literal>Sat</literal> 都会被存储到后向引用 1 中。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.repetition">
|
||
<title>重复/量词</title>
|
||
<para>
|
||
重复次数是通过量词指定的,可以紧跟在下面元素之后:
|
||
|
||
<itemizedlist>
|
||
<listitem><simpara>一个单独的字符,可能是转义的</simpara></listitem>
|
||
<listitem><simpara>点号元字符</simpara></listitem>
|
||
<listitem><simpara>一个字符类</simpara></listitem>
|
||
<listitem><simpara>一个反向引用(见下一节)</simpara></listitem>
|
||
<listitem><simpara>一个括号子模式(除非它是一个断言 - 见下一节)</simpara></listitem>
|
||
</itemizedlist>
|
||
</para>
|
||
<para>
|
||
一般的重复量词指定了一个最小数值和一个最大数值的匹配次数,
|
||
通过花括号包裹两个数字,两个数字之间用逗号隔开的语法定义。
|
||
两个数值都必须小于 65536, 并且第一个数字必须小于等于第二个。 比如:
|
||
|
||
<literal>z{2,4}</literal>
|
||
|
||
匹配 ”zz”, “zzz”, “zzzz”。 单个的右花括号不是特殊字符。
|
||
如果第二个数字被省略,但是逗号仍然存在,就代表没有上限;
|
||
如果第二个数字和逗号都被省略,那么这个量词就限定的是一个确定次数的匹配。
|
||
比如
|
||
|
||
<literal>[aeiou]{3,}</literal>
|
||
|
||
匹配至少三个连续的元音字母,但是同时也可以匹配更多,
|
||
而
|
||
|
||
<literal>\d{8}</literal>
|
||
|
||
则只能匹配 8 个数字。
|
||
|
||
</para>
|
||
<simpara>
|
||
PHP 8.4.0 之前,如果左花括号出现在不允许使用量词的位置,或者与量词语法不匹配时,则将其视为普通字符。例如
|
||
<literal>{,6}</literal> 不是量词,而是四个字符的文字字符串。
|
||
|
||
自 PHP 8.4.0 起,PCRE 扩展捆绑 PCRE2 10.44 版本,允许使用诸如 <literal>\d{,8}</literal>
|
||
之类的模式,并且将其解释为 <literal>\d{0,8}</literal>。
|
||
|
||
此外,自 PHP 8.4.0 起,量词周围允许使用空格字符,例如 <literal>\d{0 , 8}</literal> 和 <literal>\d{ 0 , 8 }</literal>。
|
||
</simpara>
|
||
<para>
|
||
允许使用量词 {0},会导致表达式的行为就像前一项和量词不存在一样。
|
||
</para>
|
||
<para>
|
||
为了方便(和历史兼容性),三个最常用的量词都有单字符缩写。
|
||
<table>
|
||
<title>单字符量词</title>
|
||
<tgroup cols="2">
|
||
<tbody>
|
||
<row>
|
||
<entry><literal>*</literal></entry>
|
||
<entry>等价于 <literal>{0,}</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>+</literal></entry>
|
||
<entry>等价于 <literal>{1,}</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>?</literal></entry>
|
||
<entry>等价于 <literal>{0,1}</literal></entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
</para>
|
||
<para>
|
||
可以通过一个不匹配任何字符的子模式后面紧跟一个匹配 0 或多个字符的量词
|
||
来构造一个没有上限的无限循环。比如:
|
||
|
||
<literal>(a?)*</literal>
|
||
</para>
|
||
<para>
|
||
早期版本的 Perl 和 PCRE 对于这种模式会在编译期得到一个错误。然而,
|
||
由于这在某些情况下是有用的,因此现在也接受这种模式了,
|
||
但是如果任何子模式的重复确实匹配不到任何字符,循环会被强制跳出。
|
||
</para>
|
||
<para>
|
||
默认情况下,量词都是”贪婪”的,也就是说,
|
||
它们会在不导致模式匹配失败的前提下,尽可能多的匹配字符(直到最大允许的匹配次数)。
|
||
这种问题的典型示例就是尝试匹配C语言的注释。
|
||
出现在 /* 和 */ 之间的所有内容都被认为是注释, 在注释中间,
|
||
可以允许出现单独的 * 和 /。
|
||
对 C 注释匹配的一个尝试是使用模式
|
||
|
||
<literal>/\*.*\*/</literal>
|
||
,
|
||
假设将此模式应用在字符串 ”
|
||
|
||
<literal>/* first comment*/ not comment /*second
|
||
comment*/</literal>”
|
||
|
||
它会匹配到错误的结果,也就是整个字符串,
|
||
这是因为量词的贪婪性导致的,它会尝试尽可能多的匹配字符。
|
||
</para>
|
||
<para>
|
||
然而,如果一个量词紧跟着一个 ?(问号) 标记,它就会成为懒惰(非贪婪)模式,
|
||
它不再尽可能多的匹配,而是尽可能少的匹配。
|
||
因此模式
|
||
|
||
<literal>/\*.*?\*/</literal>
|
||
|
||
在 C 的注释匹配上将会正确的执行。
|
||
各个量词自身的意义并不会改变,而是由于加入了 ? 使其首选的匹配次数发生改变。
|
||
不要将 ? 的这个用法和它作为量词的用法混淆。因为它又两种用法,
|
||
因此有时它会出现量词,比如
|
||
|
||
<literal>\d??\d</literal>
|
||
|
||
会更倾向于匹配一个数字,
|
||
但同时如果为了达到整个模式匹配的目的,它也可以接受两个数字的匹配。译注:以模式 \w\d??\d\w 为例,对于字符串 ”a33a”,虽然 \d?? 是非贪婪的,
|
||
但由于如果使用贪婪会导致整个模式不匹配,所以,
|
||
最终它选择的仍然是匹配到一个数字。
|
||
</para>
|
||
<para>
|
||
如果 <link linkend="reference.pcre.pattern.modifiers">PCRE_UNGREEDY</link> 选项被设置(一个在 perl 中不可用的选项),
|
||
那么量词默认情况下就是非贪婪的了。但是,
|
||
单个的量词可以通过紧跟一个 ? 来使其成为贪婪的。换句话说,
|
||
PCRE_UNGREEDY 这个选项逆转了贪婪的默认行为。
|
||
</para>
|
||
<para>
|
||
量词后面紧跟一个 ”<literal>+</literal>” 是”占有”性。它会吃掉尽可能多的字符,
|
||
并且不关注后面的其他模式,比如 <literal>.*abc</literal> 匹配 ”aabc”,
|
||
但是 <literal>.*+abc</literal> 不会匹配,
|
||
因为 <literal>.*+</literal> 会吃掉整个字符串,从而导致后面剩余的模式得不到匹配。
|
||
可以使用占有符 (+) 修饰量词来达到提升速度的目的。
|
||
</para>
|
||
<para>
|
||
当一个子组受最小数量大于 1 或有一个最大数量限制的量词修饰时,
|
||
按照最小或最大的数量的比例需要更多的存储用于编译模式。
|
||
</para>
|
||
<para>
|
||
如果一个模式以 .* 或 .{0,} 开始并且 <link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link> 选项开启(等价于 Perl 的 /s),
|
||
也就是允许 . 匹配换行符,那么模式会隐式的紧固,因为不管怎么样,
|
||
接下来都会对目标字符串中的每个字符位置进行尝试,因此在第一次之后,
|
||
在任何位置都不会有一个对所有匹配重试的点。 PCRE 会想对待 \A 一样处理这个模式。
|
||
在我们已知目标字符串没有包含换行符的情况下,
|
||
当模式以 .* 开始的时候我们为了获得这个优化,值得设置
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>,
|
||
或者选择使用 ^ 明确指明锚定。
|
||
</para>
|
||
<para>
|
||
译注:这里的优化指模式不匹配之后,不会回头再来查找下一个位置,
|
||
比如没有设置 PCRE_DOTALL,并且目标字符串第一个字符时换行符,
|
||
那么模式尝试第一个字符,发现不匹配,
|
||
会重新用模式从第二个字符位置开始进行尝试。 而使用了PCRE_DOTALL后,
|
||
是肯定匹配的….同理,当使用了 ^ 或者 /A的限定是,模式一旦不匹配,都可以直接退出,
|
||
而不用在目标字符串下一个位置再一次开始整个模式的匹配。
|
||
</para>
|
||
<para>
|
||
当一个捕获子组时重复的时,捕获到的该子组的结果是最后一次迭代捕获的值。比如,
|
||
|
||
<literal>(tweedle[dume]{3}\s*)+</literal>
|
||
|
||
匹配字符串 ”tweedledum tweedledee”,
|
||
得到的的子组捕获结果是 ”tweedledee”。然而,如果是嵌套的捕获子组,
|
||
相应的捕获值可能会被设置到之前的迭代中。
|
||
比如,
|
||
|
||
<literal>/(a|(b))+/</literal>
|
||
|
||
匹配字符串 ”aba”,
|
||
第二个捕获子组得到的结果会是 ”b”。译注:以例子说明,
|
||
b 是第二个子组最后一次捕获到的结果,所以, 第二个子组最后结果是 b,
|
||
这是符合”然而”之前描述的规则的。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.back-references">
|
||
<title>后向引用</title>
|
||
<para>
|
||
在一个字符类外面,
|
||
反斜线紧跟一个大于 0 (可能还有一位数)的数字就是一个到模式中之前出现的某个捕获组的后向引用。
|
||
</para>
|
||
<para>
|
||
如果紧跟反斜线的数字小于
|
||
10,它总是一个后向引用,并且如果在模式中没有足够多的捕获组,将会引发一个错误。换而言之,被引用的括号不能少于被引用的小于
|
||
10 的数量。
|
||
A "forward back reference" can make sense when a repetition
|
||
is involved and the subpattern to the right has participated
|
||
in an earlier iteration.
|
||
查看<link linkend="regexp.reference.escape">转义序列</link>查看具体的数字处理方式。
|
||
</para>
|
||
<para>
|
||
一个后向引用会直接匹配被引用捕获组在目标字符串中实际捕获到的内容,
|
||
而不是匹配子组模式的内容。因此,模式
|
||
|
||
<literal>(sens|respons)e and \1ibility</literal>
|
||
|
||
将会匹配 ”sense and sensibility” 和 ”response and responsibility”,
|
||
而不会匹配 ”sense and responsibility”。
|
||
如果在后向引用时被强制进行了大小写敏感匹配,
|
||
比如
|
||
|
||
<literal>((?i)rah)\s+\1</literal>
|
||
|
||
匹配 ”rah
|
||
rah”和”RAH RAH”,但是不会匹配 ”RAH rah”,
|
||
即使原始捕获子组自身是不区分大小写的。 译注:
|
||
这里其实要考虑的是后向引用期望得到的内容是和那个被引用的捕获子组得到的内容是完全一致的(当然,
|
||
我们可以通过在后向引用之前设定内部选项使其不区分大小写,或者增加模式修饰符,
|
||
同样可以达到不区分大小写的目的,但是,
|
||
这种做法实际上是从外部对其行为进行了控制。)
|
||
</para>
|
||
<para>
|
||
可能会有超过一个的后向引用引用相同的子组。
|
||
一个子组可能并不会真正的用于特定的匹配,此时,
|
||
任何对这个子组的后向引用也都会失败。 比如,
|
||
模式
|
||
|
||
<literal>(a|(bc))\2</literal>
|
||
|
||
总是在匹配 ”a” 开头而不是 ”bc” 开头的字符串时失败。
|
||
因为可能会有多达 99 个后向引用,
|
||
所有紧跟反斜线后的数字都可能是一个潜在的后向引用计数。
|
||
如果模式在后向引用之后紧接着还是一个数值字符,
|
||
那么必须使用一些分隔符用于终结后向引用语法。 如果设置了 <link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link> 选项,
|
||
可以使用空格来做。其他情况下可以使用一个空的注释。
|
||
</para>
|
||
<para>
|
||
如果一个后向引用出现在它所引用的子组内部, 它的匹配就会失败。比如,
|
||
(a\1) 就不会得到任何匹配。然而这种引用可以用于内部的子模式重复。比如,
|
||
模式
|
||
|
||
<literal>(a|b\1)+</literal>
|
||
|
||
会匹配任意数量的 ”a” 组成的字符串以及 ”aba”, “ababba” 等等(译注:
|
||
因为子组内部有一个可选路径,可选路径中有一条路能够完成匹配,在匹配完成后,
|
||
后向引用就能够引用到内容了)。在每次子模式的迭代过程中,
|
||
后向引用匹配上一次迭代时这个子组匹配到的字符串。为了做这种工作,
|
||
模式必须满足这样一个条件,模式在第一次迭代的时候,
|
||
必须能够保证不需要匹配后向引用。
|
||
这种条件可以像上面的例子用可选路径来实现,也可以通过使用最小值为 0
|
||
的量词修饰后向引用的方式来完成。
|
||
</para>
|
||
<para>
|
||
转义序列 <literal>\g</literal> 可以用于子模式的绝对引用和相对引用。
|
||
这个转义序列必须紧跟一个无符号数字或一个负数,
|
||
可以选择性的使用括号对数字进行包裹。 序列<literal>\1</literal>,
|
||
<literal>\g1</literal>,<literal>\g{1}</literal> 之间是同义词关系。
|
||
这种用法可以消除使用反斜线紧跟数值描述反向引用时候产生的歧义。
|
||
这种转义序列有利于区分后向引用和八进制数字字符,
|
||
也使得后向引用后面紧跟一个原文匹配数字变的更明了,比如
|
||
<literal>\g{2}1</literal>。
|
||
</para>
|
||
<para>
|
||
<literal>\g</literal> 转义序列紧跟一个负数代表一个相对的后向引用。比如:
|
||
<literal>(foo)(bar)\g{-1}</literal> 可以匹配字符串 ”foobarbar”,
|
||
<literal>(foo)(bar)\g{-2}</literal> 可以匹配 ”foobarfoo”。
|
||
这在长的模式中作为一个可选方案,
|
||
用来保持对之前一个特定子组的引用的子组序号的追踪。
|
||
</para>
|
||
<para>
|
||
后向引用也支持使用子组名称的语法方式描述:
|
||
<literal>(?P=name)</literal>、<literal>\k<name></literal>、
|
||
<literal>\k’name’</literal>、<literal>\k{name}</literal>、
|
||
<literal>\g{name}</literal>、<literal>\g<name></literal>、
|
||
<literal>\g'name'</literal>。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.assertions">
|
||
<title>断言</title>
|
||
<para>
|
||
一个断言就是一个对当前匹配位置之前或之后的字符的测试,
|
||
它不会实际消耗任何字符。简单的断言代码有\b、\B、 \A、 \Z、\z、 ^、$ 等,在
|
||
<link linkend="regexp.reference.escape">转义序列(反斜线)</link> 中有描述。
|
||
更加复杂的断言以子组的方式编码。 它有两种类型:
|
||
<emphasis>前瞻断言</emphasis>(从当前位置向前测试)和<emphasis>后瞻断言</emphasis>(从当前位置向后测试)。
|
||
</para>
|
||
<para>
|
||
一个断言子组的匹配还是通过普通方式进行的,
|
||
不同在于它不会导致当前的匹配点发生改变。
|
||
<emphasis>前瞻断言</emphasis>中的正面断言(断言此匹配为真)以 ”(?=” 开始,消极断言以 ”(?!” 开头。比如,
|
||
|
||
<literal>\w+(?=;)</literal>
|
||
|
||
匹配一个单词紧跟着一个分号但是匹配结果不会包含分号,
|
||
|
||
<literal>foo(?!bar)</literal>
|
||
|
||
匹配所有后面没有紧跟 ”bar” 的 ”foo” 字符串。
|
||
注意一个类似的模式
|
||
|
||
<literal>(?!foo)bar</literal>
|
||
|
||
它不能用于查找之前出现所有不是 ”foo” 的 ”bar” 匹配,
|
||
它会查找到任意的 ”bar” 出现的情况,
|
||
因为 (?!foo) 这个断言在接下来三个字符时 ”bar” 的时候是永远都 &true; 的。
|
||
前瞻断言需要达到的就是这样的效果。
|
||
</para>
|
||
<para>
|
||
<emphasis>后瞻断言</emphasis>中的正面断言以”(?<=”开始, 消极断言以”(?<!”开始。比如,
|
||
|
||
<literal>(?<!foo)bar</literal>
|
||
|
||
用于查找任何前面不是 ”foo” 的 ”bar”。
|
||
后瞻断言的内容被严格限制为只能用于匹配定长字符串。但是,如果有多个可选分支,
|
||
它们不需要拥有相同的长度。比如
|
||
|
||
<literal>(?<=bullock|donkey)</literal>
|
||
|
||
是允许的,
|
||
但是
|
||
|
||
<literal>(?<!dogs?|cats?)</literal>
|
||
|
||
将会引发一个编译期的错误。在最上级分支可以匹配不同长度的字符串是允许的。
|
||
相比较于 perl 5.005 而言,它会要求多个分支使用相同长度的字符串匹配。
|
||
|
||
<literal>(?<=ab(c|de))</literal>
|
||
|
||
这样的断言是不允许的,
|
||
因为它单个的顶级分支可以匹配两个不同的长度,
|
||
但是它可以接受使用两个顶级分支的写法
|
||
|
||
<literal>(?<=abc|abde)</literal>
|
||
|
||
这样的断言实现,
|
||
对于每个可选分支,暂时将当前位置移动到尝试匹配的当前位置之前的固定宽度处。
|
||
如果在当前没有足够的字符就视为匹配失败。后瞻断言与一次性子组结合使用可以用来匹配字符串结尾;
|
||
一个例子就是在一次性子组上给出字符串结尾。
|
||
</para>
|
||
<para>
|
||
多个断言(任意顺序)可以同时出现。
|
||
比如
|
||
|
||
<literal>(?<=\d{3})(?<!999)foo</literal>
|
||
|
||
匹配前面有三个数字但不是 ”999” 的字符串 ”foo”。注意,
|
||
每个断言独立应用到对目标字符串该点的匹配。 首先它会检查前面的三位都是数字,
|
||
然后检查这三位不是 ”999”。
|
||
这个模式不能匹配 ”foo” 前面有三位数字然后紧跟 3 位非 999 共 6 个字符的字符串,比如,
|
||
它不匹配 ”123abcfoo”。
|
||
匹配 ”123abcfoo” 这个字符串的模式可以是
|
||
|
||
<literal>(?<=\d{3}…)(?<!999)foo</literal>。
|
||
</para>
|
||
<para>
|
||
这种情况下,第一个断言查看(当前匹配点)前面的 6 个字符,检查前三个是数字,
|
||
然后第二个断言检查(当前匹配点)前三个字符不是 ”999”。
|
||
</para>
|
||
<para>
|
||
断言可以以任意复杂度嵌套。
|
||
比如
|
||
|
||
<literal>(?<=(?<!foo)bar)baz</literal>
|
||
|
||
匹配前面有 ”bar” 但是 ”bar” 前面没有 ”foo” 的 ”baz”。
|
||
另外一个模式
|
||
|
||
<literal>(?<=\d{3}...(?<!999))foo</literal>
|
||
|
||
则匹配前面有三个数字字符紧跟 3 个不是 999 的任意字符的 ”foo”。
|
||
</para>
|
||
<para>
|
||
断言子组时非捕获子组,并且不能用量词修饰,
|
||
因为对同一件事做多次断言是没有意义的.如果所有的断言都包含一个捕获子组,
|
||
那么为了在整个模式中捕获子组计数的目的,它们都会被计算在内。然而,
|
||
子字符串的捕获仅可以用于正面断言,因为对于消极的断言是没有意义的。
|
||
</para>
|
||
<para>
|
||
将断言计算在内,可以拥有的最大子组数量是 200 个。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.onlyonce">
|
||
<title>一次性子组</title>
|
||
<para>
|
||
对于同时有最大值和最小值量词限制的重复项, 在匹配失败后,
|
||
紧接着会以另外一个重复次数重新评估是否能使模式匹配。
|
||
当模式的作者明确知道执行上没有问题时,
|
||
通过改变匹配的行为或者使其更早的匹配失败以阻止这种行为是很有用的。
|
||
</para>
|
||
<para>
|
||
考虑一个例子,模式 \d+foo 应用到目标行
|
||
|
||
<literal>123456bar</literal>
|
||
时:
|
||
</para>
|
||
<para>
|
||
在匹配了 6 个数字后匹配 ”foo” 时失败,通常的行为时匹配器尝试使 \d+ 只匹配 5 个数字,
|
||
只匹配 4 个数字,在最终失败之前依次进行尝试。 一次性子组提供了一种特殊的意义,
|
||
当模式的一部分得到匹配后,不再对其进行重新评估,
|
||
因此匹配器在第一次匹配 ”foo” 失败后就能立刻失败。语法符号是另外一种特殊的括号,
|
||
以 (?> 开始,比如
|
||
|
||
<literal>(?>\d+)bar</literal>
|
||
</para>
|
||
<para>
|
||
这种括号对模式的一部分提供了”锁定”,当它包含一个匹配之后,
|
||
会阻止未来模式失败后对它内部的后向回溯。后向回溯在这里失效,
|
||
其他工作照常进行。
|
||
</para>
|
||
<para>
|
||
换一种说法,如果在目标字符串中当前匹配点是锚点,
|
||
这种类型的子组匹配的字符串等同于一个独立的模式匹配。
|
||
</para>
|
||
<para>
|
||
一次性子组不是捕获子组。如上面的例子,简单而言,
|
||
就是尽其所能吃掉尽可能多的匹配字符。因此,
|
||
尽管 \d+ 和 \d+? 都会调整要匹配的数字的个数以便模式的其他部分匹配,
|
||
(?>\d+) 却仅能匹配整个数字序列。
|
||
</para>
|
||
<para>
|
||
这个(语法)结构可以包含任意复杂度的字符, 也可以嵌套。
|
||
</para>
|
||
<para>
|
||
一次性子组可以和后瞻断言结合使用来指定在目标字符串末尾的有效匹配。
|
||
考虑当一个简单的模式比如
|
||
|
||
<literal>abcd$</literal>
|
||
|
||
应用到一个不匹配的长字符串上。
|
||
由于匹配时从左到右处理的,
|
||
PCRE会从目标中查找每一个 ”a” 然后查看是否紧接着会匹配模式的剩余部分。
|
||
如果模式是
|
||
|
||
<literal>^.*abcd$</literal>
|
||
|
||
,
|
||
那么初始的 .* 将首先匹配整个字符串,但是当它失败后(因为紧接着不是 ”a”),
|
||
它会回溯所有的匹配,依次吐出最后 1 个字符,倒数第 2 个字符等等。
|
||
从右向左查找整个字符串中的 ”a”, 因此,我们不能很好的退出。然而,
|
||
如果模式写作
|
||
|
||
<literal>^(?>.*)(?<=abcd)</literal>
|
||
|
||
那么它就不会回溯 .* 这一部分,
|
||
它仅仅用于匹配整个字符串。后瞻断言对字符串末尾的后四个字符做了一个测试。
|
||
如果它失败,匹配立即失败。对于长字符串,
|
||
这个模式将会带来显著的处理时间上的性能提升。
|
||
</para>
|
||
<para>
|
||
当一个模式中包含一个子组自己可以无限重复并且内部有无限重复元素时,
|
||
使用一次性子组是避免一些失败匹配消耗大量时间的唯一途径。
|
||
模式
|
||
|
||
<literal>(\D+|<\d+>)*[!?]</literal>
|
||
|
||
匹配一个不限制数目的非数字字符或由 <> 闭合的数字字符紧跟着 ! 或 ?。
|
||
当它匹配的时候,运行时快速的。然而,
|
||
如果它应用到
|
||
|
||
<literal>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</literal>
|
||
|
||
上将会在报告错误之前消耗很多时间。
|
||
这是因为字符串可以用于两种重复规则,并且需要为两种重复规则都分配进行尝试。
|
||
(示例的结尾使用 [!?] 而不是单个的字符,
|
||
是因为 PCRE 和 perl 都会对模式最后是一个单独字符时的快速报错有优化。
|
||
它们会记录最后需要匹配的单个字符,当它们没有出现在字符串中时快速报错。)
|
||
如果模式修改为
|
||
|
||
<literal>((?>\D+)|<\d+>)*[!?]</literal>
|
||
|
||
就会快速得到报错。(译注:
|
||
对于这里给出的模式,当目标字符串更长的时候,消耗时间会迅速增加,慎用。)
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.conditional">
|
||
<title>条件子组</title>
|
||
<para>
|
||
可以使匹配器根据一个断言的结果,
|
||
或者之前的一个捕获子组是否匹配来条件式的匹配一个子组或者在两个可选子组中选择。
|
||
条件子组的两种语法如下:
|
||
</para>
|
||
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[
|
||
(?(condition)yes-pattern)
|
||
(?(condition)yes-pattern|no-pattern)
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
<para>
|
||
如果条件满足,使用 yes-pattern,其他情况使用 no-pattern(如果指定了)。
|
||
如果有超过 2 个的可选子组,会产生给一个编译期错误。
|
||
</para>
|
||
<para>
|
||
条件一共有两种。如果在(condition)的括号内是数字组成的文本,
|
||
条件在该数字代表的(之前的)子组得到匹配时满足(即使用 yes-pattern)。
|
||
考虑下面的模式,
|
||
为了使其易于阅读其中增加了一些空白字符(查看<link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>
|
||
选项)并且将其分为三个部分:
|
||
</para>
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[
|
||
( \( )? [^()]+ (?(1) \) )
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
<para>
|
||
模式的第一部分匹配一个可选的左括号,并且如果该字符出现,
|
||
设置其为第一个子组的捕获子串。第二部分匹配一个或多个非括号字符。
|
||
第三部分是一个条件子组,它会测试第一个子组是否匹配,如果匹配到了,
|
||
也就是说目标字符串以左括号开始,条件为&true;,
|
||
那么使用 yes-pattern 也就是这里需要匹配一个右括号。其他情况下,
|
||
既然 no-pattern 没有出现,这个子组就不匹配任何东西。换句话说,
|
||
这个模式匹配一个没有括号的或者闭合括号包裹的字符序列。
|
||
</para>
|
||
<para>
|
||
如果条件式字符串 <literal>(R)</literal>,它在得到对模式或子模式的递归调用时满足。
|
||
在”最上级”,条件总是false。
|
||
</para>
|
||
<para>
|
||
如果条件不是数字序列或(R),它就必须是一个断言。这里的断言可以使任意的,积极,
|
||
消极,正向,后向都是可以的。考虑这个模式, 同样为了方便阅读,
|
||
增加了一些空白字符,并且在第二行有两个可选路径。
|
||
</para>
|
||
|
||
<informalexample>
|
||
<programlisting>
|
||
<![CDATA[
|
||
(?(?=[^a-z]*[a-z])
|
||
\d{2}-[a-z]{3}-\d{2} | \d{2}-\d{2}-\d{2} )
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
<para>
|
||
条件式一个正向积极断言,匹配一个可选的非小写字母字符序列紧接着一个小写字母。
|
||
换一种说法,它测试目标中至少出现一个小写字母,如果小写字母发现,
|
||
目标匹配第一个可选分支,其他情况下它匹配第二个分支。
|
||
这个模式匹配两种格式的字符串:dd-aaa-dd 或 dd-dd-dd。aaa 代表小写字母,
|
||
dd 是数字。
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.comments">
|
||
<title>注释</title>
|
||
<para>
|
||
字符序列(?#标记开始一个注释直到遇到一个右括号。不允许嵌套括号。
|
||
注释中的字符不会作为模式的一部分参与匹配。
|
||
</para>
|
||
<para>
|
||
如果设置了 <link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link> 选项,
|
||
一个字符类外部的未转义的 # 字符就代表本行剩余部分为注释。
|
||
</para>
|
||
<para>
|
||
<example>
|
||
<title>PCRE 模式中注释的用法</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
$subject = 'test';
|
||
|
||
/* (?# 可用于添加注释而不启用 PCRE_EXTENDED */
|
||
$match = preg_match('/te(?# this is a comment)st/', $subject);
|
||
var_dump($match);
|
||
|
||
/* 除非启用 PCRE_EXTENDED,否则空格和 # 视为模式的一部分 */
|
||
$match = preg_match('/te #~~~~
|
||
st/', $subject);
|
||
var_dump($match);
|
||
|
||
/* 启用 PCRE_EXTENDED 后,所有空白数据字符以及
|
||
同一行未转义的 # 后面的任何内容都将会忽略 */
|
||
$match = preg_match('/te #~~~~
|
||
st/x', $subject);
|
||
var_dump($match);
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
int(1)
|
||
int(0)
|
||
int(1)
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.recursive">
|
||
<title>递归模式</title>
|
||
<para>
|
||
考虑匹配圆括号内字符串的问题,允许无限嵌套括号。如果不使用递归,
|
||
最好的方式是使用一个模式匹配固定深度的嵌套。它不能处理任意深度的嵌套。 perl
|
||
5.6 提供了一个实验性的功能允许正则表达式递归。
|
||
特殊项 (?R) 提供了递归的这种特殊用法。
|
||
这个PCRE模式解决了圆括号问题(假设
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link> 选项被设置了,
|
||
因此空白字符被忽略):
|
||
|
||
<literal>\( ( (?>[^()]+) | (?R) )* \)</literal>
|
||
</para>
|
||
<para>
|
||
首先,它匹配一个左括号。
|
||
然后它匹配任意数量的非括号字符序列或一个模式自身的递归匹配(比如,
|
||
一个正确的括号子串),最终,匹配一个右括号。
|
||
</para>
|
||
<para>
|
||
这个例子模式包含无限重复的嵌套,因此使用了一次性子组匹配非括号字符,
|
||
这在模式应用到模式不匹配的字符串时非常重要。比如,
|
||
当它应用到
|
||
|
||
<literal>(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()</literal>
|
||
|
||
时就会很快的产生”不匹配”结果。
|
||
然而,如果不使用一次性子组,这个匹配将会运行很长时间,
|
||
因为有很多途径让 + 和 * 重复限定分隔目标字符串,
|
||
并且在报告失败之前需要测试所有路径。
|
||
</para>
|
||
<para>
|
||
所有捕获子组最终被设置的捕获值都是从递归最外层子模式捕获的值。
|
||
如果上面的模式匹配
|
||
|
||
<literal>(ab(cd)ef)</literal>
|
||
|
||
,捕获子组最终被设置的值为 ”ef”,
|
||
即顶级得到的最后一个值。如果增加了额外的括号
|
||
|
||
<literal>\( ( ( (?>[^()]+) | (?R) )* ) \)</literal>
|
||
捕获到的字符串就是顶层括号的匹配内容 ”ab(cd)ef”。
|
||
如果在模式中有超过 15 个捕获括号,
|
||
PCRE 在递归期间就会使用 pcre_malloc 分配额外的内存来存储数据,
|
||
随后通过 pcre_free 释放他们。如果没有内存可被分配,它就仅保存前 15 个捕获括号,
|
||
在递归内部无法给出内存不够用的错误。
|
||
</para>
|
||
|
||
<para>
|
||
<literal>(?1)</literal>、<literal>(?2)</literal> 等可以用于递归子组。
|
||
这同样可以用于命名子组:
|
||
<literal>(?P>name)</literal> 或 <literal>(?P&name)</literal>。
|
||
</para>
|
||
<para>
|
||
如果递归子组语法在它提到的子组括号外部使用(无论是子组数字序号还是子组名称),
|
||
这个操作就相当于程序设计语言中的子程序。
|
||
前面一些有一个例子指出模式 <literal>(sens|respons)e and \1ibility</literal> 匹配 ”sense and
|
||
responsibility” 和 ”response and responsibility”,但是不匹配 ”sense and
|
||
responsibility”。如果用模式 <literal>(sens|respons)e and
|
||
(?1)ibility</literal> 替代,
|
||
它会像匹配那两个字符串一样匹配 ”sense and responsibility”。
|
||
这种引用方式意义是紧接着匹配引用的子模式。(译注:
|
||
后向引用只匹配引用的子组之前匹配的结果,
|
||
这里的递归语法引用是拿引用的子模式重新匹配。)
|
||
</para>
|
||
|
||
<para>
|
||
目标字符串的最大长度是 int 型变量可以存储的最大正整数。然而,
|
||
PCRE 使用递归处理子组和无限重复。
|
||
这就是说对于某些模式可用的栈空间可能会受目标字符串限制。
|
||
</para>
|
||
|
||
</section>
|
||
|
||
<section xml:id="regexp.reference.performance">
|
||
<title>性能</title>
|
||
<para>
|
||
模式中一些项可能比其他一些更加高效。
|
||
比如使用 [aeiou] 这样的字符类会比可选路径 (a|e|i|o|u) 高效。 一般而言,
|
||
用尽可能简单的构造描述需求是最高效的。 Jeffrey
|
||
Friedl 书(精通正则表达式)中包含了很多关于正则表达式性能的讨论。
|
||
</para>
|
||
<para>
|
||
当一个模式以 .* 开始并且设置了 <link
|
||
linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>
|
||
选项时,模式通过PCRE隐式锚定,
|
||
因为它可以匹配字符串的开始。然而,如果
|
||
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>
|
||
没有设置,PCRE 不能做这个优化,因为.元字符不能匹配换行符,如果目标字符串包含换行符,
|
||
模式可能会从一个换行符后面开始匹配,而不是最开始位置。 比如,模式
|
||
|
||
<literal>(.*) second</literal>
|
||
|
||
匹配目标字符串 ”first\nand
|
||
second”(\n 是一个换行符)第一个捕获子组结果是 ”and”。为了这样做,
|
||
PCRE 尝试从目标字符串中每个换行符后开始匹配。
|
||
</para>
|
||
<para>
|
||
如果你使用模式匹配没有换行符的目标字符串,
|
||
可以通过设置 <link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>
|
||
或以 ^.* 开始的模式明确指示锚定以获取最佳性能。
|
||
这样节省了 PCRE 沿目标字符串扫描查找换行符重新开始的时间。
|
||
</para>
|
||
<para>
|
||
小心模式中的无限重复嵌套。这在应用到不匹配字符串时可能会导致运行时间很长。
|
||
考虑模式片段
|
||
|
||
<literal>(a+)*</literal>
|
||
</para>
|
||
<para>
|
||
这个模式可以有 33 种方式匹配 ”aaaa”,
|
||
并且这个数字会随着字符串的长度的增加迅速增加. (*重复可以匹配0,1,2,3,4次,
|
||
并且除了0外每种情况+都有不同次数的匹配对应)。
|
||
当模式的剩余部分导致整个匹配失败的时候, PCRE原则上回尝试每种可能的变化,
|
||
这将会非常耗时。
|
||
</para>
|
||
<para>
|
||
对于一些简单的情况的优化是像
|
||
|
||
<literal>(a+)*b</literal>
|
||
|
||
这样紧接着使用原文字符串.。
|
||
在着手正式匹配工作之前,PCRE 检查目标字符串后面是否有 ”b” 字符,
|
||
如果没有就立即失败。然而当紧接着没有原文字符的时候这个优化是不可用的。
|
||
你可以比较观察
|
||
|
||
<literal>(a+)*\d</literal>
|
||
|
||
和上面模式的行为差异。
|
||
前者在应用到整行的 ”a” 组成的字符串时几乎是立即报告失败,
|
||
而后者在目标字符串长于 20 个字符时,时间消耗就相当可观。
|
||
</para>
|
||
</section>
|
||
</chapter>
|
||
|
||
<!-- Keep this comment at the end of the file
|
||
Local variables:
|
||
mode: sgml
|
||
sgml-omittag:t
|
||
sgml-shorttag:t
|
||
sgml-minimize-attributes:nil
|
||
sgml-always-quote-attributes:t
|
||
sgml-indent-step:1
|
||
sgml-indent-data:t
|
||
indent-tabs-mode:nil
|
||
sgml-parent-document:nil
|
||
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
||
sgml-exposed-tags:nil
|
||
sgml-local-catalogs:nil
|
||
sgml-local-ecat-files:nil
|
||
End:
|
||
vim600: syn=xml fen fdm=syntax fdl=2 si
|
||
vim: et tw=78 syn=sgml
|
||
vi: ts=1 sw=1
|
||
-->
|