独学而无友,则孤陋而寡闻!
分类:
2009-03-21 16:43:41
这里谈到的区别是就 Perl 5.005 来说的。
PCRE 提供了一些对 Perl 正则表达式机制的扩展:
下面说明 PCRE 所支持的正则表达式的语法和语义。Perl 文档和很多其它书中也解说了正则表达式,有的书中有很多例子。Jeffrey Friedl 写的“Mastering Regular Expressions”,由 O'Reilly 出版社发行(ISBN 1-56592-257-3),包含了大量细节。这里的说明只是个参考文档。
正则表达式是从左向右去匹配目标字符串的一组模式。大多数字符在模式中表示它们自身并匹配目标中相应的字符。作为一个小例子,模式 The quick brown fox 匹配了目标字符串中与其完全相同的一部分。
正则表达式的威力在于其能够在模式中包含选择和循环。它们通过使用元字符来编码在模式中,元字符不代表其自身,它们用一些特殊的方式来解析。
有两组不同的元字符:一种是模式中除了方括号内都能被识别的,还有一种是在方括号内被识别的。方括号之外的元字符有这些:
反斜线字符有几种用途。首先,如果其后跟着一个非字母数字字符,则取消该字符可能具有的任何特殊含义。此种将反斜线用作转义字符的用法适用于无论是字符类之中还是之外。
例如,如果想匹配一个“*”字符,则在模式中用“\*”。这适用于无论下一个字符是否会被当作元字符来解释,因此在非字母数字字符之前加上一个“\”来指明该字符就代表其本身总是安全的。尤其是,如果要匹配一个反斜线,用“\\”。
Note: 单引号或双引号括起来的 PHP 中的反斜线有特殊含义。因此必须用正则表达式的 \\ 来匹配 \,而在 PHP 代码中要用 "\\\\" 或 '\\\\'。
如果模式编译时加上了 选项,模式中的空白字符(字符类中以外的)以及字符类之外的“#”到换行符之间的字符都被忽略。可以用转义的反斜线将空白字符或者“#”字符包括到模式中去。
反斜线的第二种用途提供了一种在模式中以可见方式去编码不可打印字符的方法。并没有不可打印字符出现的限制,除了代表模式结束的二进制零以外。但用文本编辑器来准备模式的时候,通常用以下的转义序列来表示那些二进制字符更容易一些:
“\cx”的精确效果如下:如果“x”是小写字母,则被转换为大写字母。接着字符中的第 6 位(0x40)被反转。从而“\cz”成为 0x1A,但“\c{”成为 0x3B,而“\c;”成为 0x7B。
在“\x”之后最多再读取两个十六进制数字(其中的字母可以是大写或小写)。在 UTF-8 模式下,允许用“\x{...}”,花括号中的内容是表示十六进制数字的字符串。原来的十六进制转义序列 \xhh 如果其值大于 127 的话则匹配了一个双字节 UTF-8 字符。
在“\0”之后最多再读取两个八进制数字。以上两种情况下,如果少于两个数字,则只使用已出现的。因此序列“\0\x\07”代表两个二进制的零加一个 BEL 字符。如果是八进制数字则确保在开始的零后面再提供两个数字。
处理反斜线后面跟着一个不是 0 的数字比较复杂。在字符类之外,PCRE 以十进制数字读取该数字及其后面的数字。如果数字小于 10,或者之前表达式中捕获到至少该数字的左圆括号,则这个序列将被作为逆向引用。有关此如何运作的说明在后面,以及括号内的子模式。
在字符类之中,或者如果十进制数字大于 9 并且之前没有那么多捕获的子模式,PCRE 重新从反斜线开始读取其后的最多三个八进制数字,并以最低位的 8 个比特产生出一个单一字节。任何其后的数字都代表自身。例如:
注意八进制值 100 或更大的值之前不能以零打头,因为不会读取(反斜线后)超过三个八进制数字。
所有的定义了一个单一字节的序列可以用于字符类之中或之外。此外,在字符类之中,序列“\b”被解释为反斜线字符(0x08),而在字符类之外有不同含义(见下面)。
反斜线的第三个用法是指定通用字符类型:
任何一个转义序列将完整的字符组合分割成两个分离的部分。任一给定的字符匹配一个且仅一个转义序列。
“字”的字符是指任何一个字母或数字或下划线,也就是说,任何可以是 Perl "word" 的字符。字母和数字的定义由 PCRE 字符表控制,可能会根据指定区域的匹配而改变。举例说,在 "fr" (French) 区域,某些编码大于 128 的字符用来表示重音字母,这些字符能够被 \w 所匹配。
这些字符类型序列可以出现在字符类之中和之外。每一个匹配相应类型中的一个字符。如果当前匹配点在目标字符串的结尾,以上所有匹配都失败,因为没有字符可供匹配。
反斜线的第四个用法是某些简单的断言。断言是指在一个匹配中的特定位置必须达到的条件,并不会消耗目标字符串中的任何字符。子模式中更复杂的断言的用法在下面描述。反斜线的断言有:
这些断言可能不能出现在字符类中(但是注意 "\b" 有不同的含义,在字符类之中也就是反斜线字符)。
字边界是目标字符串中的一个位置,其当前字符和前一个字符不能同时匹配 \w 或者 \W(也就是其中一个匹配 \w 而另一个匹配 \W),或者是字符串的开头或结尾,假如第一个或最后一个字符匹配 \w 的话。
\A,\Z 和 \z 断言与传统的音调符和美元符(下面说明)的不同之处在于它们仅匹配目标字符串的绝对开头和结尾而不管设定了任何选项。它们不受 或 选项的影响。\Z 和 \z 的不同之处在于 \Z 匹配了作为字符串最后一个字符的换行符之前以及字符串的结尾,而 \z 仅匹配字符串的结尾。
\G 断言仅在当前匹配位置是匹配开始那一点时为真,如 的 offset 参数指定那样。当 offset 的值非零时这和 \A 不同。自 PHP 4.3.3 起可用。
\Q 和 \E 自 PHP 4.3.3 起可被用来在模式中忽略正则表达式匹配字符。例如:\w+\Q.$.\E$ 将匹配一个或多个可组成字的字符,其后接着的是字面上的 .$. 并且位于字符串末尾。
自 PHP 4.4.0 和 5.1.0 起,当选择了 additional escape sequences to match generic character types are available UTF-8 模式时有三种更多的转移序列可用:
以上由 xx 所表示的属性名限于 Unicode 通用类型属性。每个字符具有一个此种属性,由两个缩写字母指定。为和 Perl 兼容,可以在左花括号和属性名中间加入一个上箭头符号来表示排除属性。例如 \p{^Lu} 就和 \P{Lu} 相同。
如果在 \p 或 \P 中只用了一个字母,则包括了所有该字母开头的属性。此情况下,如果不是排除的话,可以省略花括号。下面例子中的两项具有相同效果:
\p{L}
\pL
C | Other - 其它 |
Cc | Control - 控制 |
Cf | Format - 格式 |
Cn | Unassigned - 无符号 |
Co | Private use - 私有 |
Cs | Surrogate - 代替 |
L | Letter -字母 |
Ll | Lower case letter - 小写字母 |
Lm | Modifier letter - 修正符字母 |
Lo | Other letter - 其它字母 |
Lt | Title case letter - 标题大写字母 |
Lu | Upper case letter - 大写字母 |
M | Mark - 标记 |
Mc | Spacing mark - 空格标记 |
Me | Enclosing mark - 环绕标记 |
Mn | Non-spacing mark - 非空格标记 |
N | Number - 数字 |
Nd | Decimal number - 十进制数字 |
Nl | Letter number - 字母数字 |
No | Other number - 其它数字 |
P | Punctuation - 标点符号 |
Pc | Connector punctuation - 连接标点符 |
Pd | Dash punctuation - 横线标点符 |
Pe | Close punctuation - 结束标点符 |
Pf | Final punctuation - 最终标点符 |
Pi | Initial punctuation - 起始标点符 |
Po | Other punctuation - 其它标点符号 |
Ps | Open punctuation - 开始标点符 |
S | Symbol - 符号 |
Sc | Currency symbol - 货币符号 |
Sk | Modifier symbol - 修正符号 |
Sm | Mathematical symbol - 算术符号 |
So | Other symbol - 其它符号 |
Z | Separator - 分隔符 |
Zl | Line separator - 行分隔符 |
Zp | Paragraph separator - 段落分隔符 |
Zs | Space separator - 空格分隔符 |
PCRE 不支持扩展属性例如 "Greek" 或 "InMusicalSymbols"。
指定不区分大小写的匹配不影响此类转义序列。例如 \p{Lu} 总是仅和大写字母匹配。
\X 转移符匹配能组成扩展 Unicode 序列的任何数目的 Unicode 字符。\X 和 (?>\PM\pM*) 相同。
也就是,它匹配一个没有“mark”属性,后面跟着零或多个具有“mark”属性的字符,并且将此序列看成原子组(见下)。典型的具有“mark”属性的字母是影响到前面的字符的重音符。
用 Unicode 属性来匹配字符并不快,因为 PCRE 不得不搜索一个包含超过一万五千字符数据的结构。这正是为什么在 PCRE 中传统的转义序列例如 \d 和 \w 不使用 Unicode 属性。
在字符类之外,默认匹配模式下,音调符是一个仅在当前匹配点是目标字符串的开头时才为真的断言。在字符类之中,音调符的含义完全不同(见下面)。
如果涉及到几选一时音调符不需要是模式的第一个字符,但如果出现在某个分支中则应该是该选择分支的第一个字符。如果所有的选择分支都以音调符开头,这就是说,如果模式限制为只匹配目标的开头,那么这是一个紧固模式。(也有其它结构可以使模式成为紧固的。)
美元符是一个仅在当前匹配点是目标字符串的结尾或者当最后一个字符是换行符时其前面的位置时为 TRUE 的断言(默认情况下)。如果涉及到几选一时美元符不需要是模式的最后一个字符,但应该是其出现的分支中的最后一个字符。美元符在字符类之中没有特殊含义。
美元符的含义可被改变使其仅匹配字符串的结尾,只要在编译或匹配时设定了 选项即可。这并不影响 \Z 断言。
如果设定了 选项则音调符和美元符的含义被改变了。此种情况下,它们分别匹配紧接着内部 "\n" 字符的之后和之前,再加上目标字符串的开头和结尾。例如模式 /^abc$/ 在多行模式下匹配了目标字符串 "def\nabc",但正常时不匹配。因此,由于所有分支都以 "^" 开头而在单行模式下成为紧固的模式在多行模式下为非紧固的。如果设定了 ,则 选项会被忽略。
注意 \A,\Z 和 \z 序列在两种情况下都可以用来匹配目标的开头和结尾,如果模式所有的分支都以 \A 开始则其总是紧固的,不论是否设定了 。
在字符类之外,模式中的圆点可以匹配目标中的任何一个字符,包括不可打印字符,但不匹配换行符(默认情况下)。如果设定了 则圆点也会匹配换行符。处理圆点与处理音调符和美元符是完全独立的,唯一的联系就是它们都涉及到换行符。圆点在字符类之中没有特殊含义。
\C 可以用来匹配单一字节。在 UTF-8 模式下这有意义,因为句号可以匹配由多个字节组成的整个字符。
左方括号开始了一个字符类,右方括号结束之。单独一个右方括号不是特殊字符。如果在字符类之中需要一个右方括号,则其应该是字符类中的第一个字符(如果有音调符的话,则紧接音调符之后),或者用反斜线转义。
字符类匹配目标中的一个字符,该字符必须是字符类定义的字符集中的一个;除非字符类中的第一个字符是音调符,此情况下目标字符必须不在字符类定义的字符集中。如果在字符类中需要音调符本身,则其必须不是第一个字符,或用反斜线转义。
举例说,字符类 [aeiou] 匹配了任何一个小写元音字母,而 [^aeiou] 匹配了任何一个不是小写元音字母的字符。注意音调符只是一个通过枚举指定那些不在字符类之中的字符的符号。不是断言:仍旧会消耗掉目标字符串中的一个字符,如果当前位置在字符串结尾的话则失败。
当设定了不区分大小写的匹配时,字符类中的任何字母同时代表了其大小写形式,因此举例说,小写的 [aeiou] 同时匹配了 "A" 和 "a",小写的 [^aeiou] 不匹配 "A",但区分大小写时则会匹配。
换行符在字符类中不会特殊对待,不论 或者 选项设定了什么值。形如 [^a] 的字符类总是能够和换行符相匹配的。
减号(-)字符可以在字符类中指定一个字符范围。例如,[d-m] 匹配了 d 和 m 之间的任何字符,包括两者。如果字符类中需要减号本身,则必须用反斜线转义或者放到一个不能被解释为指定范围的位置,典型的位置是字符类中的第一个或最后一个字符。
字面上的 "]" 不可能被当成字符范围的结束。形如 [W-]46] 的模式会被解释为包括两个字符的字符类("W" and "-")后面跟着字符串 "46]",因此其会匹配 "W46]" 或者 "-46]"。然而,如果将 "]" 用反斜线转义,则会被当成范围的结束来解释。因此 [W-\]46] 会被解释为一个字符类,包含有一个范围以及两个单独的字符。八进制或十六进制表示的 "]" 也可以用来表示范围的结束。
范围是以 ASCII 比较顺序来操作的。也可以用于用数字表示的字符,例如 [\000-\037]。在不区分大小写匹配中如果范围里包括了字母,则同时匹配大小写字母。例如 [W-c] 等价于 [][\^_`wxyzabc] 不区分大小写地匹配。如果使用了 "fr" 区域的字符表,[\xc8-\xcb] 匹配了大小写的重音 E 字符。
字符类型 \d,\D,\s,\S,\w 和 \W 也可以出现于字符类中,并将其所能匹配的字符添加进字符类中。例如,[\dABCDEF] 匹配了任何十六进制数字。用音调符可以很方便地制定严格的字符集,例如 [^\W_] 匹配了任何字母或数字,但不匹配下划线。
任何除了 \,-,^(位于开头)以及结束的 ] 之外的非字母数字字符在字符类中都没有特殊含义,但是将它们转义也没有坏处。
竖线字符用来分隔多选一模式。例如,模式:
gilbert|sullivan
,,, 和 的设定可以在模式内部通过包含在 "(?" 和 ")" 之间的 Perl 选项字母序列来改变。选项字母为:
i | 代表 |
m | 代表 |
s | 代表 |
x | 代表 |
U | 代表 |
X | 代表 |
例如,(?im) 设定了不区分大小写,多行匹配。也可以通过在字母前加上减号来取消这些选项。例如组合的选项 (?im-sx),设定了 和 ,并取消了 和 。如果一个字母在减号之前与之后都出现了,则该选项被取消设定。
如果选项改变出现于顶层(即不在子模式的括号中),则改变应用于其后的剩余模式。因此 /ab(?i)c/ 只匹配 "abc" 和 and "abC"。此行为是自 PHP 4.3.3 起绑定的 PCRE 4.0 中被修改的。在此版本之前 /ab(?i)c/ 的执行与 /abc/i 相同(例如匹配 "ABC" 和 "aBc")。
如果选项改变出现于子模式中,则效果不同。这是 Perl 5.005 的行为的一个变化。子模式中的选项改变只影响到子模式内部其后的部分,因此 (a(?i)b)c 将只匹配 "abc" 和 "aBc"(假定没有使用 )。这意味着选项在模式的不同部位可以造成不同的设定。在一个分支中的改变可以传递到同一个子模式中后面的分支中,例如 (a(?i)b|c) 将匹配 "ab","aB","c" 和 "C",尽管在匹配 "C" 的时候第一个分支会在选项设定之前就被丢弃。这是因为选项设定的效果是在编译时确定的,否则会造成非常怪异的行为。
PCRE 专用选项 和 可以和 Perl 兼容选项以同样的方式来改变,分别使用字母 U 和 X。(?X) 标记设定有些特殊,它必须出现于任何其它特性之前。最好放在最开头的位置。
子模式由圆括号定界,可以嵌套。将模式中的一部分标记为子模式可以:
1. 将多选一的分支局部化。例如,模式:
cat(aract|erpillar|)
2. 将子模式设定为捕获子模式(如同以前定义的)。当整个模式匹配时,目标字符串中匹配了子模式的部分会通过 pcre_exec() 的 ovector 参数传递回调用者。左圆括号从左到右计数(从 1 开始)以取得捕获子模式的数目。
例如,如果将字符串 "the red king" 来和模式
the ((red|white) (king|queen))
简单的括号实现两种功能的事实不总是有帮助的。经常有需要一组子模式但不需要捕获的时候。如果左括号后面跟着 "?:",子模式不做任何捕获,并且在计算任何之后捕获的子模式时也不算在内。例如,如果用字符串 "the white queen" 去和模式 the ((?:red|white) (king|queen)) 匹配,捕获的子串是 "white queen" 和 "queen",并被计为 1 和 2。所捕获的子串的最大数目是 99,所有子模式,包括捕获的和没捕获的,最大数目是 200。
作为方便的速记,如果在非捕获子模式的开头需要任何选项设定,则选项字母可以出现在 "?" 和 ":" 中间。因此下面两个模式
(?i:saturday|sunday)
(?:(?i)saturday|sunday)
匹配了完全相同的一组字符串。因为分支选项是从左向右尝试的,并且直到子模式结束前都不会重置选项,因此在一个分支中的选项设定会影响到之后的分支,所以以上模式会匹配 "SUNDAY" 和 "Saturday"。
自 PHP 4.3.3 起有可能通过 (?P
重复是由数量符指定的,可以接以下任何一项:
普通的重复数量符指定了所允许的匹配的最小和最大数目,方法是将两个数字放在花括号中,中间用逗号分隔。数字必须小于 65536,并且第一个数字必须小于或等于第二个数字。例如:z{2,4} 匹配了 "zz","zzz" 或 "zzzz"。单个的右花括号不算是特殊字符。如果省略了第二个数字但是有逗号,则表示没有上限。如果同时省略了第二个数字和逗号,则数量符指定了匹配的准确数目。因此 [aeiou]{3,} 匹配至少连续 3 个元音,但是可以匹配更多。\d{8} 则匹配了正好 8 个数字。出现在不允许放置数量符位置或者不符合数量符语法的左花括号,被当成字面上的该字符。例如 {,6} 不是一个数量符,而是字面上的这四个字符。
数量符 {0} 是允许的,导致表达式理解为前一项和数量符不存在。
为方便起见(以及历史性的兼容),三个最常用的数量符都有单字符的缩写:
* | 等同于 {0,} |
+ | 等同于 {1,} |
? | 等同于 {0,1} |
有可能通过在一个不匹配任何字符的子模式后面跟一个没有上限的数量符构造出无限循环,例如:(a?)*。
对此类模式早期版本的 Perl 和 PCRE 会在编译时给出错误。不过由于这在某些情况下有用,如今已经接受此种模式了,但是如果任何子模式的重复确实不匹配任何字符,则循环会被强制打断。
默认时,数量符是“贪吃型”(greedy)的,即会在不导致剩余模式失败的情况下尽可能多地匹配(直到所允许的数目上限)。这会出问题的经典例子是尝试匹配 C 语言的注释。在 /* 和 */ 序列中间,可能会出现单个的 * 和 / 字符。对 C 注释如果试图用 /\*.*\*/ 去和字符串 /* first comment */ not comment /* second comment */ 匹配会失败,因为由于 .* 项目的贪吃性,会匹配成整个字符串。
不过,如果在后面加一个问号数量符,则会停止贪吃性,而变成匹配尽可能少的数目,因此模式 /\*.*?\*/ 就会正确匹配 C 注释。各种数量符的含义并没有改变,只是优先的匹配数目。不要将问号的此用法和其自己作为数量符的使用混淆。因为有两种用法,有时可以两个一起出现,例如 \d??\d 会优先匹配一个数字,但如别无选择也可以匹配两个以使剩余模式匹配。
如果设定了 选项(此选项 Perl 中没有)则数量符默认不是贪吃型的,但是在个别模式后加上一个问号可以将其变成贪吃型的。换句话说,这可以反转默认的行为。
后面跟上一个 + 的数量符是“占有性”(possessive)的。它会匹配尽可能多的字符而不管剩余的模式。因此 .*abc 可以匹配 "aabc" 但是 .*+abc 就不会,因为 .*+ 已经匹配了整个字符串。自 PHP 4.3.3 起可以用占有性数量符可以来加快处理过程。