4.6
正则表达式的介绍
PHP支持两种风格的正则表达式语法:POSIX和Perl。
POSIX风格的正则表达式是PHP编译时的默认风格,但可以通过编译PCRE(Perl-copatible regular expression,Perl兼容正则表达式)库来使用Perl风格。我们将简单介绍POSIX的风格,但如果你已经是一位Perl程序员,或者希望了解更多关于PCRE的内容,可以阅读在线手册:
http://php.net。
提示:POSIX正则表达式更容易掌握,但是它们不是二进制安全的。
4.6.1 基础知识
正则表达式是一种描述一段文本模式的
方法。
在PHP中,匹配正则表达式更有点像strstr()匹配,而不像相等比较。因为是在一个字符串的某个位置(如果不指明则可能在字符串中的任何位置)匹配另一个字符串。
除了精确匹配匹配字符外,还可以用特殊字符来指定表达式的元意(meta-meaning)。
4.6.2 字符集和类
可以用字符.来作为一个通配符来代替除换行符(\n)之外的任一个字符。例如,正则表达式:.at, 可以与“cat”、“sat”和“mat”等进行匹配。通常,这种通配符匹配用于操作系统中的文件名匹配。
可以指明字符所属的一个集合。在前面的例子中,如果要限定它是a到z之间的字符,就可以像下面这样指明:[a-z]at
任何包含在方括号([])中的内容都是一个字符集——一个被匹配字符所属的字符集合。请注意,方括号中的表达式只匹配一个字符。
我们可以列出一个集合,例如:[aeiou]
也可以描述一个范围,正如前面用连字符那样,也可是一个范围集:[a-zA-Z]
此外,还可以用集合来指明字符不属于某个集。例如:[^a-z]。脱字符号(^)包括在方括号里面时,表示否。
许多预定义字符类也可以在正则表达式中使用,用于POSIX风格的正则表达式的字符类有:[[:alnum:]],文字数字字符;[[:alpha:]],字母字符;[[:lower:]],小写字母;[[:upper:]],大写字母;[[:digit:]],小数;[[:xdigit:]],十六进制数字;[[:punct:]],标点符号;[[:blank:]],制表符和空格;[[:space:]],空白字符;[[:cntrl:]],控制符;[[:print:]],所有可打印的字符;[[:graph:]],除空格外所有可打印的字符。
4.6.3 重复
通常,读者会希望指明某个字符串或字符类将不止一次地出现。可以在正则表达式中使用两个特殊字符代替。符号“*”表示这个模式可以被重复0次或更多次,符号“+”则表示这个模式可以被重复1次或更多次。这两个符号应该放在要作用的表达式的后面。例如:[[:alnum:]]+。表示“至少有一个字母字符”。
4.6.4 子表达式
通常,将一个表达式分隔为几个子表达式是非常有用的,例如,可以表示“至少这些字符串中的一个需要精确匹配”。可以使用圆括号来实现,与在数学表达式中的方法一样。例如:(very )*large。可以匹配“large”、“very large”、“very very large”等等。
4.6.5 子表达式计数
可以用在花括号({})中的数字表达式来指定内容允许重复的次数。可以指定一个确切的重复次数({3}表示重复3次),或者一个重复次数的范围({2,4}表示重复2~4次),或是一个开底域的重复范围({2, }表示至少要重复两次)。
例如:(very ){1,3},表示匹配“very ”、“very very ”和“very very very ”。
4.6.6 定位到字符串的开始或末尾
也可以确定一个特定的子字符串是否出现在开始、末尾或在两个位置都出现。当要确定字符串中只有要找的单词而没有其他单词出现时,它将相当有用。
脱字符号(^)用于正则表达式的开始,表示字符串必须出现在被搜索字符串的开始处,字符“$”用于正则表达式的末尾,表示子字符串必须出现在字符串的末尾。
例如,以下是在字符串开始处匹配bob:^bob。
这个模式将匹配com出现在字符串末尾处的字符串:com$
最后,这个模式将匹配只包含a到z之间一个字符的字符串:^[a-z]$
4.6.7 分支
可以使用正则表达式中的一条竖线来表示一个选择。例如,如果要匹配com、edu或net,就可以使用如下所示的表达式:com|edu|net
4.6.8 匹配特殊字符
如果要匹配本节前面提到过的特殊字符,例如“. { $”,就必须在这们前面加一个反斜杠(\)。如果要匹配一个反斜杠,则必须用两个反斜杠(\\)来表示。
在PHP中,必须将正则表达式模式包括在一个单引号字符串中。使用双引号引用的正则表达式将带来一些不必要的复杂性。PHP还使用反斜杠来转义特殊字符——例如反斜杠。如果希望在模式中匹配一个反斜杠,必须使用两个反斜杠来表示它是一个反斜杠字符,而不是一个转义字符。
同样,由于相同的原因,如果希望在一个双引号引用的PHP字符串中使用反斜杠字符,必须使用两个反斜杠。这可能会有些混淆,这样要求的结果将是表示一个包含了反斜杠字符的正则表达式的一个PHP字符串需要4个反斜杠。PHP解释器将这4个反斜杠解释成两个。然后,由正则表达式解释器解析为一个。
$符号也是双引号引用的PHP字符串和正则表达式的特殊字符。要使一个$字符能够在模式中匹配,必须使用“\\\$”。因为这个字符串被引用在双引号中,PHP解释器将其解析为\$,还正则表达式解释器将其解析成一个$字符。
4.6.9 特殊字符一览
在POSIX正则表达式中,用于方括号外面特殊字符的摘要:
\,转义字符;^,在字符串开始匹配;$,在字符串末尾匹配;.,匹配除换行符(\n)之外的字符;|,选择分支的开始(读为或);(,子模式的开始;),子模式的结束;*,重复0次或更多次;+,重复一次或更多次;{,最小/最大量记号的开始;},最小/最大量记号的结束;?,标记一个子模式为可选的。
在POSIX正则表达式中,用于方括号里面特殊字符的摘要:
\,转义字符;^,非,仅用在开始位置;-,用于指明字符范围。
4.6.10 在智能表单中应用
在智能表单应用程序中,正则表达式至少有两种用途。第一种用途是在顾客的反馈中查找特定的名词。使用一个字符串函数,如果希望匹配“shop”、“customer service”或“retail”,就必须做3次不同的搜索。如果使用一个正则表达式,就可以同时匹配所有3 个,如下所示:shop|customer service|retail
第二个用途是验证程序中用户的电子邮件地址,这需要通过用正则表达式来对电子邮件地址的标准格式进行编码。它的编码如下所示:^[a-zA-Z0-9_\-.]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-.]+$
“^[a-zA-Z0-9_\-.]+”表示“至少由一个字母、数字、下划线、连字符、点号或者这些字符组合为开始的字符串”。
请注意,当一个在一个字符类的开始或末尾处使用点号时,它将失去其特殊通配符的意义,只能成为一个点号字符。符号“@”匹配字符“@”。
“[a-zA-Z0-9\-]+”与包含文字数字字符和连字符的主机名匹配。
请注意,我们去除了连字符,因为它是方括号内的特殊字符。
“\.”匹配“.”字符。我们在字符类外部使用点号,因此必须对其转义,使其能够匹配一个点号字符。
“[a-zA-Z0-9\-.]+$”匹配域名的剩下部分,它包含字母、数字和连字符,如果需要还可包含更多的点号直到字符串的末尾。
不难发现,有时一个无效的电子邮件地址也会符合这个正则表达式。找到所有的无效电子邮件几乎是不可能的,但是经过分析,情形将会有所改善。可以按许多不同的方式精化这个表达式。例如,可以列出所有有效的顶级域(TLD)。
当对某些对象进行限制的时候,请千万小心,因为可能排斥1%的有效数据的校验函数比允许出现10%的无效数据的校验函数还要麻烦。
4.7 用正则表达式查找子字符串
查找子字符串是正则表达式的主要应用。在PHP中,可以使用的并且用于匹配POSIX风格正则表达式的两个函数是ereg()和eregi()。ereg()函数原型如下所示:int ereg(string pattern, string search, array [matches]);
该函数搜索字符串search,在pattern中寻找与正则表达式相匹配的字符串。如果发现了与pattern的子表达式相匹配的字符串,这些字符串将会存储在数组matches中,每个数组元素对应一个子表达式。
函数eregi()除了不区分大小写外,其他功能与ereg()一样。
4.8 用正则表达式替换子字符串
与前面使用的str_replace()函数一样,也可以使用正则表达式来查找和替换子字符串。在正则表达式中,可以使用的两个函数是ereg_replace()和eregi_replace(),其原型如下所示:string ereg_replace(string pattern, string replacement, string search);
该函数在字符串search中查找正则表达式pattern的字符串,并且用字符串replacement来替换。
函数eregi_replace()除了不区分大小写分,其他与ereg_replace()相同。
4.9 使用正则表达式分割字符串
另一个实用的正则表达式函数是split(),它的原型如下所示:array split(string pattern, string search[, int max]);
这个函数将字符串search分割成符合正则表达式模式的子字符串,然后将子符串返回到一个数组中。整数max指定进入数组中的元素个数。
该函数对分割电子邮件地址、域名或日期是非常有用的。例如:
$address =
'username@example.com';
$arr = split ('\./@', $address);
while (list($key, $value) = each ($arr)
echo '<br />'.$value;
以上
代码将主机名分割为5个部分并将它们分别输出到一行。
4.10 比较字符串函数和正则表达式函数
一般而言,对于同样的功能,正则表达式函数运行效率要低于字符串函数。如果应用程序足够简单,那么就用字符串表达式。但是,对于可以通过单个正则表达式执行的任务来说,如果使用多个字符串函数,则是不对的。
4.12 下一章