PHP学习笔记之正则表达式
最近开始正式学习PHP,由于刚入门,之前零零散散的在网上搜索,对php正则表达式这一块的内容没有一个系统的认识,所以现在做一下小小的总结,我看的书是《PHP和MySQL Web开发》(原书第四版),很厚一本,趁着寒假读完吧。
一、基础知识
-
正则表达式是一种描述一段文本模式的方法。
-
在PHP中,匹配正则表达式有点像 strstr() 匹配,而不像相等比较,因为是在一个字符串的某个位置(如果不指明则可能在字符串中的任何位置)匹配另一个字符串。
例如,字符串"shop"既可以匹配正则表达式"shop",也可以匹配正则表达式"h","ho"等。 -
除了精确匹配字符外,还可以用特殊字符来指定表达式的元意。
例如,使用特殊字符,可以指定一个在字符串开始或末尾肯定存在的模式,该模式的某部分可能被重复,或模式中的字符属于特定的某一类型。此外,还可以按特殊字符的出现来匹配。
二、字符集和类
字符集可以用于匹配属于特定类型的任何字符;事实上是一种通配符。
例如:
.at
可以与"cat"、“sat”、"mat"进行匹配。(通常这种通配符匹配用于操作系统的文件名匹配)
但上述同样可以匹配‘#at’,如果要限定是a到z之间的字符,则可以像下面这样指明:
[a-z]at
- 任何被包含在方括号([ ])中的内容都是一个字符类——一个被匹配字符所属的字符集合。注意,方括号中的表达式只匹配一个字符。例如,
[aeiou] //表示元音字母
[a-zA-Z] //表示任何的大小写字母
此外,还可以用集合来指明字符不属于某个集。当把脱字符号 (^) 包括在方括号里面时,表示否。例如,
[^a-z] //匹配任何不在a到z之间的字符
三、重复
通常,程序员会希望指明某个字符串或字符类将不止一次地出现,在正则表达式中可使用两个特殊字符代替。
- 符号 * 表示这个模式可以被重复0次或更多次,符号 + 则表示这个模式可以被重复1次或更多次。这两个符号均要放到表达式的后面。例如,
[[a-zA-z]] + //表示至少有一个英文字母
四、子表达式
- 通常,将一个表达式分割为几个子表达式是非常有用的。例如,可以表示“至少这些字符串中的一个需要精确匹配”。可以使用圆括号来实现,与数学表达式中的方法一样。例如,
(very)*large //可以匹配“large”、“very large”、“very very large”等
- 除此之外,可以在花括号({ })中的数字表达式来指定内容允许重复的次数。例如,
(very ){1, 3} //表示重复1~3次,即匹配“very”、“very very”和“very very very”
(very ){3} //表示重复3次,即匹配“very very very”
(very ){2, } //表示至少重复2次,即匹配“very very”和“very very very···”及更多。
五、定位到字符串的开始或末尾
- 脱字符号(^)用于正则表达式的开始,表示子字符串必须出现在被搜索字符的开始处。
- 字符"$"用于正则表达式的末尾,表示子字符串必须出现在字符串的末尾。
- 也可以确定一个特定的子表达式是否出现在开始、末尾或在两个位置都出现,当要确定字符串中只有要找的单词而没有其他单词出现时,可以这么使用。
例如,
^bob //在字符串开始时匹配bob
com$ //将匹配com出现在字符串末尾处的字符串
^[a-z]$ //匹配只包含a到z之间一个字符的字符串
六、分支
可以使用正则表达式中的一条竖线来表示一个选择。例如,要批评com、edu或net,可以使用如下所使表达式:
com|edu|net
七、匹配特殊字符
如果要匹配前面提到过的特殊字符,如,. 、{ 或 $,就必须在它们前面加一个反斜杠(\)。如果要匹配一个反斜杠,则必须用两个反斜杠(\)来表示。
注:在PHP中,最好将正则表达式模式包括在一个单引号字符串中,使用双引号引用的表达式将带来一些不必要的麻烦。这里为避免混淆,不具体说明,如有兴趣,可自行搜索了解。
下面用表格总结一下特殊字符在方括号内/外的含义:
表1 在POSIX正则表达式中,用于方括号外面的特殊字符摘要
字符 | 意义 | 字符 | 意义 |
---|---|---|---|
\ | 转义字符 | ) | 子模式的结束 |
^ | 在字符串开始匹配 | * | 重复0次或更多次 |
$ | 在字符串末尾匹配 | + | 重复1次或更多次 |
. | 匹配除换行符(\n)之外的字符 | { | 最小/最大量记号的开始 |
| | 选择分支的开始(读为或) | } | 最小/最大量记号的结束 |
( | 子模式的开始 | ? | 标记一个子模式为可选的 |
表2 在POSIX正则表达式中,用于方括号里面的特殊字符摘要
字符 | 意义 |
---|---|
\ | 转义字符 |
^ | 非,仅用在开始位置 |
- | 用于指明字符范围 |
八、正则表达式的应用
- 第一个用途是查找特定的名词,使用一个正则表达式同时匹配多个字符串。例如,
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\-.]+$ 匹配域名剩下部分,它包含字母、数字和连字符,如果需要还可以包含更多的点号直到字符串的末尾。
九、用正则表达式查找子字符串
用正则表达式查找子字符串,这是正则表达式的主要应用。
在PHP中,可以使用的并且用于匹配POSIX风格正则表达式的两个函数是ereg()和eregi()。( eregi()除了不区分大小写外,其他功能与ereg()一样 )
ereg()函数原型如下:
int ereg(string pattern, string search, array [matches]);
该函数搜索字符串serch,在pattern中寻找与正则表达式相匹配的字符串。如果发现了pattern的子表达式相匹配的字符串,这些字符将会存储在数组matches中,每个数组元素对应一个子表达式。
例如:
if (!eregi( '^[a-zA-Z0-9_\-.]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-.]+$', $email)){
echo "<p>That is not a valid eamil address.</p>";
exit;
}
注:PHP中的ereg()函数存在截断漏洞,在CTF比赛中可能会用到,可参考下面这篇文章ereg()截断漏洞
十、用正则表达式替换子字符串
正则表达式也可用来查找和替换子字符串,与此相关的两个函数是ereg_replace()和eregi_replace(),其原型如下所示:
string ereg_replace(string pattern, string replacement, string serch);
该函数在字符串serch中查找正则表达式pattern的字符串,并用字符串replacement来替换。
函数eregi_replace()除了不区分大小写外,其他与ereg_replace()相同。
十一、用正则表达式分割字符串
另一个实用的正则表达式函数是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个部分并将它们分别输出到一行:
username
@
example
.
com