The Preg Function Interface
PHP正则引擎的处理方式完全是程序式的(☞95),包括表10-2顶端的6个函数,表格还列举了4个有用的函数,将在本章后面提到。
表10-2:PHP Preg函数概览
每个函数的具体功能都取决于参数的个数、标志位(flag),以及正则表达式所使用的模式修饰符。在深入细节之前我们先通过几个例子来看看PHP中的正则表达式的例子和处理方式。
最后的程序,如果输入‘Larry,·Curly,· Moe’,返回三个元素的数组:‘Larry’,‘Curly’和‘Moe’。
“pattern”参数
/"Pattern/"Arguments
所有preg函数的第一个参数都是pattern,正则表达式包含在一对分隔符之内,可能还跟有模式修饰符。在上面的第一个例子中,pattern参数是‘/<tableb/i’,也就是包含在一对斜线(分隔符)里头的「<tableb」,然后是模式修饰符i(不区分大小写的匹配)。
PHP单引号字符串
因为正则表达式很有可能包含反斜线,,所以在以字符串文字方式提供pattern参数时,最好使用PHP的单引号字符串。第3章介绍了PHP的字符串文字(☞103),简单地说,如果使用单引号字符串文本,正则表达式就可以省略许多额外的转义。PHP 的单引号字符串只有两个元序列,‘’’和‘//’,分别代表单引号和反斜线。
有一种转义需要特别注意,就是在正则表达式中使用「//」匹配一个反斜线字符。在单引号字符串中,每个「」都应表示为//,所以「//」就成了////。四个反斜线才能匹配一个反斜线字符,这真神奇!
(473页有反斜线繁复到极端的例子。)
举个具体的例子,用正则表达式匹配Windows系统中的分区名,例如‘C:’。可以用正则表达式「^[A-Z]://$」,表示为单引号字符串文字就是‘^[A-Z]:////$’。
第5章第190页有一个例子,「^.*//」作为pattern字符串时应该写成‘/^.*///’,使用3个反斜线。照此类推,我找到这几个例子:
头两个例子尽管方式不同,结果却是一样的。在第一个例子中,结尾的‘/’/'对于单引号字符串文字并不是特殊文本,所以它就等于字符串的值。第二个例子中,‘//’对于字符串文字来说有特殊含义,所以输出的字符串中出现单个‘’。它与后面的字符(斜线)放在一起,得到与第一个例子同样的‘/’。同样的道理,第三个和第四个例子也会得到同样的结果。
当然,你也可以使用PHP的双引号字符串文本,但是它们要麻烦许多。它们支持许多字符串元序列,这些在正则表达式字符串中都必须特殊处理。
分隔符
preg引擎要求正则表达式两端必须有分隔符,因为设计者希望它看起来更像Perl,尤其在模式修饰符的使用方法上更是如此。有的程序员觉得在正则表达式两端添加分隔符简直是多此一举,但是无论好还是不好,规定就是规定(第448也给出了一个“不好”的原因)。
常见的做法是使用斜线作为分隔符,不过我们还可以用除了字母、数字、反斜线和空白字符之外的任何ASCII字符做分隔符。最常见的是一对斜线,但两个‘!’和‘#’也很常见。
如果第一个分隔符表示“开(opening)”:
{(< [
对应的“闭”分隔符就是:
}) >]
如果使用这样“配对”的分隔符,分隔符就可能嵌套,所以‘((d+))’也可以用作pattern字符串。其中,外面的括号是模式字符串分隔符,内部的括号属于分隔符之内的正则表达式。为了清晰起见,我会避免这种情况,使用简单易懂的‘/(d+)/’。
pattern字符串内部可以出现转义的分隔符,所以‘/<B>(.*?)</B>/i’并没有错,不过换一组分隔符可能看得更清楚,例如‘!<B>(.*?)</B>!i’使用‘!…!’作为分隔符,而‘{<B>(.*?)</B>}i’使用‘{…}’。
模式修饰符
在结束分隔符之后可以跟随多种模式修饰符(用PHP的术语来说,叫做pattern modifier),在某些情况下,修饰符也可以出现在正则表达式内部,修饰模式的某些性质。我们已经在一些例子中看到过表示不区分大小写的模式修饰符i。下面简要介绍模式修饰符:
下面三个很少用到
表达式内部的模式修饰符 在正则表达式内部,模式修饰符可以单独出现,来启用或停用某些特性(例如用「(?i)」来启用不区分大小写的匹配,用「(?-i)」来停用☞135)。此时,它们的作用范围持续到对应的结束括号,如果不存在,就持续到正则表达式的末尾。
他们也可以用作模式修饰范围(☞135),例如「(?i:…)」表示对此括号内的内容进行不区分大小写的匹配,「(?-sm:…)」表示在此范围内停用s和m模式。
正则表达式之外,结束分隔符之后的模式修饰符可以以任何顺序组织,下例中的‘si’表示同时启用不区分大小写和点号通配模式:
if (preg_match(/'{<title>(.*?)</title>}si/',$html,$captures))
PHP特有的修饰符 列表最上端的4个模式修饰符属于标准修饰符,在第3章(☞110)已经讨论过。修饰符e只能在preg_replace中使用,详细的讨论见对应的小节(☞459)。
模式修饰符u告诉preg引擎,以UTF-8编码处理正则表达式和目标字符串。此模式修饰符不会修改数据,只是更改正则引擎处理数据的方式。默认(也就是未使用模式修饰符u)的情况下,preg引擎认为接收的数据都是8位编码的(☞87)。如果用户知道数据是UTF-8编码的,请使用此修饰符,否则请不要使用。在UTF-8编码中,非ASCII字符以多个字节来存储,使用u修饰符能够确保多个字节会被作为单个字符来处理。
模式修饰符X启用PCRE的“额外功能(extra stuff)”,目前它只有一个效果:如果出现了无法识别的反斜线序列,就报告错误。例如,默认情况下,「k」在PCRE中没有特殊意义,就等价于「k」(因为这不是一个已知的元序列,所以反斜线会被忽略)。如果使用了模式修饰符X,就会报告“unrecognized character follows”。
未来版本的 PHP 可能包含更高版本的 PCRE,其中当前没有特殊意义的反斜线组合可能被赋予新的意义,所以为了保持未来的兼容性(以及一般可读性),最好是不要转义不需要的字母,除非它们现在有特殊意义。从这个意义上说,模式修饰符X 意义重大,因为它可以发现这样的错误。
模式修饰符S调用PCRE的“study(研究)”特性,预先分析正则表达式,在某些顺利的情况下,在尝试匹配时速度会大大提升。本章中关于效率的内容将对此有介绍,请参考第478页。
剩下的模式修饰符实用价值不大,也不常用:
●模式修饰符A把匹配锚定在第一次尝试的位置,就等于整个正则表达式以「G」开头。如果用第4章的汽车的类比,这就是关闭传动机构的“驱动过程”(☞148)。
●模式修饰符D会把每个「$」替换为「z」(☞112),即「$」匹配字符串的末尾,而不是字符串之内的换行符。
●模式修饰符 U 交换元字符的匹配优先含义:「*」和「*?」交换,「+」和「+?」交换,等等。我猜这个模式修饰符的主要作用在于制造混乱,所以我完全不推荐使用它。