PHP正则表达式

一、 正则表达式介绍

描述了一类字符串的特征,然后通过这个特征可以配合一些特定的函数,来完成对字符串更加复杂的一系列操作!
普通字符和特殊字符组成的一个字符串

1. 用途:匹配、查找、替换、分割 
2. php提供了两套正则表达式函数库 
    (1). Perl 兼容正则表达式函数(推荐使用) 
    (2). POSIX 扩展正则表达式函数

二、语法规则

表达式的格式: "/表达式/[修正符]" 

其中修正符是可选的,表示对表达式做额外的修饰。
我们一般习惯使用正斜线”/“作为定界的字符,前后一致
注意:定界符不可以是字母、数字和斜线\。
像“#”、“|”、“!”等都可以的
如:/…/ #…# |….|

三、 正则表达式的组成部分:

1. 基本单位(原子)

原子是组成正则表达式的基本单位,在分析正则表达式时,应作为一个整体。
原子包括以下内容:

* 单个字符、数字,如a-z,A-Z,0-9。 
* 模式单元,如(ABC)可以理解为由多个原子组成的大的原子。 
* 原子表,如 [ABC]。 
* 重新使用的模式单元,如:\\1 
* 普通转义字符,如:\d, \D, \w 
* 转义元字符,如:\*,\. 
* 元字符

2. 元字符(具有特殊意义字符):

各个字符含义

[] 表示单个字符的原子表 
    例如:[aoeiu] 表示任意一个元音字母 
          [0-9] 表示任意一位数字 
          [a-z][0-9]表示小写字和一位数字构成的两位字符 
          [a-zA-Z0-9] 表示任意一位大小字母或数字 
[^] 表示除中括号内原子之外的任何字符 是[]的取反 
    例如:[^0-9] 表示任意一位非数字字符 
          [^a-z] 表示任意一位非小写字母 
{m}    表示对前面原子的数量控制,表示是m次 
    例如:[0-9]{4} 表示4为数字 
          [1][3-8][0-9]{9} 手机号码 
{m,} 表示对前面原子的数量控制,表示是至少m次          
    例如: [0-9]{2,} 表示两位及以上的数字 
{m,n}表示对前面原子的数量控制,表示是m到n次 
    例如: [a-z]{6,8} 表示6到8位的小写字母 
* 表示对前面原子的数量控制,表示是任意次,等价于{0,} 
+ 表示对前面原子的数量控制,表示至少1次,等价于{1,} 
? 表示对前面原子的数量控制,表示0次或1次(可有可无) 等价于{0,1} 
    例如:正整数:[1-9][0-9]* 
            整数:[\-]?[0-9]+ 
() 表示一个整体原子,还有一个子存储单元的作用【使用\\数字或$数字来代表圆括号部分所匹配到的内容】。 
        也可以使用?:来拒绝子存储。 (?:.*?) 
    例如:(red) 字串red 
           (rea|blue) 字串red或blue 
           (abc){2} 表示两个abc 
|  表示或的意思 
        (rea|blue) 字串red或blue 
^  用在正则单元块的开头处,表示必须以指定的开头 
$  用在正则单元块的结尾处,表示必须以指定的结尾 
.  表示任意一个除换行符之外的字符 

()的使用:

如果使用对应的\\数字或$数字来代表圆括号部分所匹配到的内容,那么要注意匹配完整

由于没用匹配到\\1对应的e,匹配失败

只有对应的内容都匹配到了才是匹配成功。array数组中分别保存了匹配的完整字符串,和()所匹配到的字符


常用组合(贪婪匹配问题)

.与{n}配合 中间有n个非空
.与*配合 默认是贪婪匹配(尽可能多的去匹配字符) 中间有任意个非空

.*? 可以解决贪婪匹配(表示最小匹配所有字符)

也可以使用模式修正符来解决: m

3. 普通转义字符:

\d 匹配一个数字;等价于[0-9]
\D 匹配除数字以外任何一个字符;等价于[^0-9]
\w 匹配一个英文字母、数字或下划线;等价于[0-9a-zA-Z_]
\W 匹配除英文字母、数字和下划线以外任何一个字符;等价于[^0-9a-zA-Z_]
\s 匹配一个空白字符;等价于[\f\n\r\t\v]
\S 匹配除空白字符以外任何一个字符;等价于[^\f\n\r\t\v]
\f 匹配一个换页符等价于 \x0c 或 \cL
\n 匹配一个换行符;等价于 \x0a 或 \cJ
\r 匹配一个回车符等价于\x0d 或 \cM
\t 匹配一个制表符;等价于 \x09\或\cl
\v 匹配一个垂直制表符;等价于\x0b或\ck
\oNN 匹配一个八进制数字
\xNN 匹配一个十六进制数字
\cC 匹配一个控制字符

4. 常见模式修正符

常见四种

i    在和模式进行匹配时不区分大小写"/[a-zA-Z]/" <==>"/[a-z]/i" 
m    多行匹配,如果目标字符串 中没有"\n"字符, 或者模式中没有出现^或$, 设置这个修饰符不产生任何影响
s    如果设定了此修正符,那么.将匹配所有的字符包括换行符(表示匹配视为单行:就是可以让点.支持换行) 
U    禁止贪婪匹配

U 禁止贪婪匹配

i 不区分大小写

如果设置了这个修饰符,模式中的字母会进行大小写不敏感匹配。

m 多行匹配

(如果目标字符串中没用“\n”字符,或者模式中没用出现^或$,设置这个修饰符不产生任何影响)
使用条件

  1. 目标字符串必须包含换行符“\n” 字符串中出现\n就表示新的一行开始
  2. 正则表达式中必须要出现^或者$
    未使用修正符时:


此时使用模式修正符,发现没用改动!

为什么?
因为在单引号里面不识别转义\n,它认为是普通的字符,而不是换行!!!

因此,我们将单引号换成双引号(我们可以发现\n有了高亮,实现了转义)

此时发现,m 模式修饰符起到了作用
换成以test结束试试(使用$)

依旧可以

\n代表着换行那么,我们手动换行试试?

只能匹配一个,此时匹配的是最后一个
为什么?
解释:
在windows操作系统中,我们所看到的换行(肉眼看到的换行现象),其实是通过两个字符来完成的(\r\n)
在linux操作系统中看,我们所看到的换行(肉眼看到的换行现象),是通过\n来完成的

如果是这样,我们修改匹配规则: $pattern=’/test\r$/m’;

发现匹配到了三个,此时应为前三个匹配到

那么我们如何在windows如何匹配到四个呢?我们可以使用*来使得\r出不出现都能匹配到

1
$pattern='/test\r*$/m';


此时成功匹配到四个

s 让点.支持换行

如果设置了这个修饰符,模式中的点号元字符匹配所有字符,包含换行符。如果没有这个 修饰符,点号不匹配换行符
不使用s 修正符时

使用s修正符:

使用手动回车代替\n时:

发现:同样我们在使用模式修正符s时,如果我们不是写\n而是手动回车也会发生同样的错误



补充:
\r 回车符
\n 换行符
两者区别
以下代码摘自网络:本帖最后由 rossini23 于 2011-05-26 23:48 编辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int main()
{
printf("this is\ra test\r\n"); //1
printf("this is\na test\r\n"); //2
printf("this is\r\na test\r\n"); //3
return 0;
}

a tests
this is
a test
this is
a test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.	#include <stdio.h>
2.
3. int main()
4. {
5. printf("this is\ra test\r\n"); //1
6. printf("this is\na test\r\n"); //2
7. printf("this is\r\na test\r\n"); //3
8. return 0;
9. }
上面这一小段代码在Linux、windows下的输出如下:
1. a tests
2. this is
3. a test
4. this is
5. a test
第一条打印语句,\r使光标移动到本行行首,然后打印a test覆盖this i,变成a tests
第二条打印语句,\n使光标移动到下一行行首,然后打印a test
第三条打印语句,\r\n使光标移动到本行行首,然后移动到下一行行首,然后打印a test

然后在linux下,用stty设置关闭ONLCR转换:
stty -onlcr
发现输出变为:
1.    a tests
2.    this is
3.             a test
4.    this is
5.    a test
看起来,第二条打印语句的\n行为是“将光标移动到下一行当前位置”
在linux系统下,onlcr是控制nl转换到cr nl。
也就是说正常情况下\n被转换成了\r\n,然后送到屏幕上显示,所以第二个输出语句看到的效果是回到下一行行首。

stty -onlcr也就是关闭nl转换到cr nl,这时候送到屏幕上的只有\n,看到结果是移动到下一行当前位置。


\r\n是一个让人郁闷的问题,windows和我们的嵌入式系统之间交互总是有问题,所以想搞明白这个问题。
网络上搜\r\n也可以看到很多OS之间、协议之间交互的问题。

四、 正则表达式的函数:

preg_grep --  返回与模式匹配的数组单元 
**preg_match_all** -- 进行全局正则表达式匹配 , 返回共计匹配的个数。 
    和下面的一样,不同的是匹配到最后(全局匹配) 
**preg_match** -- 进行正则表达式匹配,只匹配一次,返回1,否则0, 
    格式:preg_match("正则表达式","被匹配的字串",存放结果的变量名,PREG_OFFSET_CAPTURE,起始偏移量) 
    其中:PREG_OFFSET_CAPTURE表示获取匹配索引位置 
          起始偏移量:从指定位置开始匹配 
preg_quote -- 转义正则表达式字符 
preg_split -- 用正则表达式分割字符串 
**preg_replace** -- 执行正则表达式的搜索和替换

preg_match_all 执行多次

执行一个全局正则表达式匹配
搜索subject中所有匹配pattern给定正则表达式的匹配结果并且将它们以flag指定顺序输出到matches中
在第一个匹配找到后, 子序列继续从最后一次匹配位置搜索

int preg_match_all ( string $pattern , string $subject [, array &$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]]] )
参数 说明:

1.pattern

要搜索的模式,字符串形式。

2.subject

输入字符串。

3.matches

多维数组,作为输出参数输出所有匹配结果, 数组排序通过flags指定。

4.flags

可以结合下面标记使用(注意不能同时使用PREG_PATTERN_ORDER和 PREG_SET_ORDER):

PREG_PATTERN_ORDER(默认)

结果排序为$matches[0]保存完整模式的所有匹配, $matches[1] 保存第一个子组的所有匹配,以此类推。

PREG_SET_ORDER

结果排序为$matches[0]包含第一次匹配得到的所有匹配(包含子组), $matches[1]是包含第二次匹配到的所有匹配(包含子组)的数组,以此类推。

PREG_OFFSET_CAPTURE

如果这个标记被传递,每个发现的匹配返回时会增加它相对目标字符串的偏移量。 注意这会改变matches中的每一个匹配结果字符串元素,使其 成为一个第0个元素为匹配结果字符串,第1个元素为 匹配结果字符串在subject中的偏移量

5.offset

通常, 查找时从目标字符串的开始位置开始。可选参数offset用于 从目标字符串中指定位置开始搜索(单位是字节)。

preg_match 执行一次

mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

第一个参数:正则表达式:pattern
pattern:要搜索的模式。可以使一个字符串或字符串数组。
第二个参数:要替换的字符串:replacement

第三个参数:目标字符串:subject
要进行搜索和替换的字符串或字符串数组。
如果subject是一个数组,搜索和替换回在subject 的每一个元素上进行, 并且返回值也会是一个数组。

如何使中间部分不变,只将div标签替换成a标签?
联系正则表达式中的()和\\ 由于\\指的使()中的内容,所以我们可以使用\\保留原来的字符串

也可以同时替换多个

replacement中可以包含反向引用\\n 或$n,语法上首选后者。

我们还能在标签内部也使用()和对应的反斜杠或$ 来保留标签样式

第四个参数limit(可选)
每个模式在每个subject上进行替换的最大次数。默认是 -1(无限)。

第五个参数count(可选)
如果指定,将会被填充为完成的替换次数。

和str_replace一样,第一个参数和第二个参数都可以是数组
如果pattern(第一个参数)和replacement(第二个参数) 都是数组,每个pattern使用replacement中对应的 元素进行替换。如果replacement中的元素比pattern中的少, 多出来的pattern使用空字符串进行替换。