使用字符类别
类别是通过把字符放置在方括号([])内创建的。例如,你可以用[aeiou]来匹配任意一个元音字母。这等价于(a|e|i|o|u)。或者可以使用连字符来指定字符的范围:[a-z]用于匹配任意单个小写字母,[A-Z]则匹配任何大写字母,[A-Za-z]一般用于匹配任意字母,[0-9]则用于匹配任意数字。例如,[a-z]{3}将匹配abc、def、oiw等。
在类别中,除了4个元字符之外,大多数元字符都会按字面量处理。反斜杠仍然是转义符,但是如果把插入符号(^)用作类别中的第一个字符,那么它将是“非”运算符。因此,[^aeiou]将匹配任何非元音字母。类别中的另外一个元字符是短划线,它指示一个范围(如果把短划线用作类别中的最后一个字符,它就是字面上的短划线)。当然,闭方括号(])仍然具有类别终止符的意义。
自然,类别可以同时具有范围和字符字面量。可以通过[A-Z '.]表示人们的名字,其中可以包含字母、空格、撇号和句点(同样,类别中的句点不需要被转义,因为它会在这里失去其本来的意义)。
除了创建你自己的类别之外,有6个已经定义好的类别,它们具有自己的快捷方式。数字和空格类别很容易理解。
类别 | 快捷方式 | 含义 |
---|---|---|
[0-9] | \d | 任意数字 |
[^0-9] | \D | 非数字 |
[\f\r\t\n\v] | \s | 任意空白 |
[^\f\r\t\n\v] | \S | 非空白 |
[A-Za-z0-9] | \w | 任意单词字符 |
[^A-Za-z0-9] | \W | 非单词字符 |
单词字符类别并不是指语言意义上的“单词”,而是指用空格或标点符号断开的字符串。
使用这种信息,可以更轻松地把5位的数字(即邮政编码)模式写成^[0-9]{5}$或^\d{5}$。举另外一个例子,can\s?not将匹配can not和cannot(单词can,其后接着0个或一个空格字符,再接着not)。
使用字符类别
使用界限
界限是用于帮助找出值范围的快捷方式。在某种程序上,你已经见过了它:使用插入符号和美元符号来匹配值的开头和末尾。但是,如果你想匹配值内的界限,该怎么做呢?
最清晰的界限出现在单词与非单词之间。这里的“单词”不是cat、month或zeitgeist,而是在\w快捷方式意义上的字母A~Z(包括大写和小写字母)、数字0~9以及下划线。为了把单词用作界限,提供了\b快捷方式。为了把非单词字符用作界限,提供了\B快捷方式。因此,模式\bfor\b将匹配they've come for you,但是不匹配force或forebode,\bfor\B将匹配force,但是不匹配they've come for you或informal。
查找所有匹配
回到Perl兼容的正则表达式使用的PHP函数上来,使用preg_match()只是为了查看模式是否匹配值。但是脚本没有(准确地)报告值中的什么内容确实匹配模式。可以通过把一个变量用作该函数的第三个参数来查明该信息:
preg_match(pattern, subject, $match)
$match变量将包含发现的第一个匹配(由于该函数只会返回值中的第一个匹配)。为了查找所有的匹配,可以使用preg_match_all()。其语法是相同的:
preg_match_all(pattern, subject, $matches)
该函数将返回产生匹配的次数,如果没有找到一个匹配,则返回FALSE。它还将把产生的每个匹配赋予$matches。
不要太贪婪
Perl兼容的正则表达示的关键成分是贪婪(greediness)的概念,在POSIX中则没有这个概念。默认情况下,PCRE将尝试尽可能多地进行匹配。例如,模式<.+>将匹配任何HTML标签。当在像<a href="page.php">Link</a>这样的字符串上执行测试时,它实际上将匹配整个字符串,从开始<到结束>。不过,这个字符串包含三个可能的匹配:整个字符串、开始标签(从<a到">)以及结束标签(</a>)。
为了克服贪婪,可以使匹配变得懒惰(lazy)。懒惰的匹配将包含尽可能少的数据。可以通过在任意量词后接一个问号使之变懒惰。例如,模式<.+?>将返回上述字符串中的两个匹配:开始标签和结束标签。它将不会返回整个字符串作为匹配(这是正则表达式语法的令人混淆的方面之一:相同的字符(这里的问号)可以根据其上下文而具有不同的含义)。
使模式不那么贪婪的另一种方式是使用“非”类别。模式<[^>]+>将匹配开始<和结束>之间的所有内容(结束>除外)。因此,使用该模式的效果与使用<.+?>相同。该模式还将匹配包含换行符(句点会排除它)的字符串。
模式修饰符
模式修饰符不同于其他元字符,这是由于它们放在结束定界符之后。这些字符(当置于结束定界符之后时)将改变正则表达式的行为方式。
字符 | 效果 |
---|---|
A | 将模式定位到字符串的开头 |
i | 启用不区分大小写的模式 |
m | 启用多行匹配 |
s | 使句点匹配每个字符,包括换行符 |
x | 忽略大多数空白 |
U | 执行不贪婪的匹配 |
在这些定界符中,最重要的是i,它启用不区分大小写的查找。使用for的变体的所有示例都不会匹配单词For。不过,/for.*/i将是一个匹配。注意:我在该模式中包括了定界符,因为修饰符出现在结束定界符之后。类似地,该序列中的最后一步参考“不要太贪婪”,并且指出for.*?如何执行懒惰查找。/for.*/U也是如此。
多行模式也很有趣,这是由于你可以使插入符号和美元符号以不同的方式工作。默认情况下,它们都会对整个值起作用。在多行模式中,插入符号匹配任一行的开头,美元符号则匹配任一行的末尾。
使用修饰符
√提示
不管多行设置如何,为了总是匹配模式的开头和末尾,可以使用一些快捷方式。在模式内,快捷方式\A将只匹配值的开头,\z匹配值的末尾,\Z则会匹配任何行的末尾,就像单行模式中的$一样。
匹配和替换模式
虽然preg_match()和preg_match_all()这两个函数将为你查找内容,但是如果你想执行查找和替换,则需要使用preg_replace()。其语法如下:
preg_replace(pattern, replacement, subject)
该函数可以带有可选的第四个参数,用于限制执行替换的次数。
要用dog替换cat的所有实例,将使用:
$str=preg_replace('/cat/','dog','I like my cat');
这个函数返回改变过的值(如果没有产生匹配,则返回未改变过的值),因此,你可能想把它赋予一个变量,或者把它用作另一个函数的参数(例如,调用echo()打印它)。此外,提醒一下,这只是一个示例:你永远都不希望使用正则表达式用一个字符串替换另一个字面量字符串,而代之以使用str_replace()函数。
在此要讨论与这个函数有关的一个概念:后向引用(back referencing)。在邮政编码匹配模式中^(\d{5})(-\d{4})?$,圆括号内有两个分组(第一个表示必需的前5位数字,第二个表示可选的短划线以及4位数字的扩展)。在正则表达式模式内,PHP将自动从1开始对圆括号内的分组编号。后向引用允许通过使用$以及相应的编号来引用每个独立的部分。例如,如果把邮政编码94710-0001与这个模式进行匹配,后向引用$2将得到-0001。代码$0则会引用完整的初始字符串。在$matches[0]中显示完整的邮政编码匹配,在$matches[1]中显示匹配的前5个数字,并在$matches[2]中显示任何匹配的短划线以及4个数字。