PHP-正则表达式

世界上最难懂的是道士的鬼符、医生的处方和程序员的正则表达示。(ง •_•)ง

 
 

PHP 正则表达式

 
 

正则表达示我们其实之前经常看到,它主要用在以下一些地方:
1.匹配邮箱、手机号码、验证码
2.替换敏感的关键词。例如:涉及政治和骂人的话
3.文章采集。
4.早期的表情替换技术,ubb文件编码、markdown编辑器替换等
5.以后自己写模板引擎也需要用到正则表达示
其他….

正则表达式是一种描述字符串结果的语法规则,是一个特定的格式化模式,可以匹配、替换、截取匹配的字符串。常用的语言基本上都有正则表达式,如JavaScript、java等。其实,只有了解一种语言的正则使用,其他语言的正则使用起来,就相对简单些。文本主要围绕解决下面问题展开。



1. 定界符

定界符,就是定一个边界,边界以内的就是正则表达示。
PHP的正则表达示定界符的规定如下:
定界符,不能用a-zA-Z0-9\ 其他的都可以用。必须成对出现,有开始就有结束。
我们来例几个例子:

例子 说明
/中间写正则/ 正确
$中间写正则$ 正确
%中间写正则% 正确
^中间写正则^ 正确
@中间写正则@ 正确
#中间写正则# 正确
(中间写正则) 错误
A中间写正则A 错误

 
 

2. 转义字符

注:\ 是转义字符,如果在以后正则表达示里面需要匹配/,如下:
/ / /
这个时候真要匹配/ 的时候,需要把定界符里面的/ 用转义字符转义一下,写成下面的例子:
/ </font>/ /
如果你觉得麻烦,遇到这种需要转义的字符的时候可以把两个正斜线(/ /)定界,改为其他的定界符(# #)。

&emsp;
&emsp;

3. 原子

原子是正则表达示里面的最小单位,原子说白了就是需要匹配的内容。一个成立的正则表达示当中必须最少要有一个原子。
所有可见不可见的字符就是原子!
说明:我们见到的空格、回车、换行、0-9、A-Za-z、中文、标点符号、特殊符号全为原子。
在做原子的实例前我们先来讲解一个函数,preg_match:
int preg_match ( string $正则 , string $字符串 [, array &$结果] )
功能:根据$正则变量,匹配$字符串变量。如果存在则返回匹配的个数,把匹配到的结果放到$结果变量里。如果没有匹配到结果返回0。
注:上面是preg_match常用的主要几个参数。我在上面将另外几个参数没有列出来。因为,另外两个参数太不常用了。
我们来通过实验来证明:

(1)↓↓

1
2
3
4
5
6
7
8
9
10
11
12
<?php
//定义一个变量叫zz,放正则表达示。为了方便大家记忆,如果你英文比较ok,建议把变量名还是写成
//英文的$pattern
$zz = '/a/';
$string = 'ddfdjjvi2jfvkwkfi24';
if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

因为我希望的是匹配a,而$string当是是不存在a的,所以不成功。

(2)↓↓

1
2
3
4
5
6
7
8
9
10
<?php
$zz = '/wq/';
$string = 'ssssswqaaaaaa';
if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

上面的字符串中s后存在wq,因此匹配成功。

接下来我们匹配一个空格试试:
(3)↓↓

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$zz = '/ /';
$string = 'sssssw aaaaa';
if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

运行结果:
匹配到了,结果为:array(1) { [0]=> string(1) " " }

因上,$string这个变量的w字符后存在一个空格。所以匹配成功,输出了字符串类型,长度为1。只不过我们的肉眼不可见,看不到这个字符串而已。

&emsp;

4. 特殊标识的原子

原子 说明 等价式
\d 匹配一个0-9 [a-zA-Z0-9_]
\D 除了0-9以外的所有字符 [^a-zA-Z0-9_]
\w a-zA-Z0-9_ [0-9]
\W 除了0-9A-Za-z_以外的所有字符 [^0-9]
\s 匹配所有空白字符\n \t \r 空格 [ \t\n\f\r]
\S 匹配所有非空白字符 [^ \t\n\f\r]
[ ] 指定范围的原子

这个个需要记住,最好达到默写级别。记忆的时候成对记忆,\d是匹配一个0-9,那么\D 就是除了0-9以外的所有字符。上面已经说明的很清楚了,我们进行实验一步一步对这些进行学习。

&emsp;
1. \d匹配一个0-9

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$zz = '/\d/';

$string = '我爱喝9你爱不爱喝';

if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

&emsp;
2. \D匹配一个非0-9的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$zz = '/\D/';

$string = '121243中23453453';

if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

运行结果:
匹配成功,匹配到了中。因为它不是0-9之间的字符。

&emsp;
3. \w匹配一个a-zA-Z0-9_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$zz = '/\w/';

$string = '新中_国万岁呀万岁';

if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

运行结果:
匹配成功,匹配到了下划线。

&emsp;
4. \W匹配一个非a-zA-Z0-9_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$zz = '/\w/';

$string = 'afasABCWEQR44231284737';

if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

运行结果:
匹配失败。因为,上面上面全是a-zA-Z0-9_,没有非a-zA-Z0-9_。

&emsp;
5. \s 匹配所有空白字符\n \t \r 空格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$zz = '/\s/';

$string = "中国万
岁";

if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

运行结果:
匹配成功,因为有一个回车。

&emsp;
6. \S 非空字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$zz = '/\s/';

$string = "
a ";

if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

运行结果:
匹配成功。虽然上面有空格,回车和缩进。但是,有一个非空白字符a。因此,匹配成功。

&emsp;
7. [] 指定范围的原子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

$zz = '/[0-5]\w+/';

$string = '6a';

$string1 = '1C';

if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

结论:
上例中0-5匹配$string失败,而$string1成功。因为,$string中的第一个数值为6,不在[0-5]的范围之内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

$zz = '/[a-zA-Z0-9_]\w/';

$string = 'ab';

$string1 = '9A';

if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

结论:$string和$string1都匹配成功。因为\w就是[a-zA-Z0-9_]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

$zz = '/[abc]\d+/';

$string = 'a9';

$string1 = 'b1';

$string2 = 'c5';

$string3 = 'd4';


if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

结论:$string、$string1、$string2匹配成功,而$string3不成功。因为$string3超过了[abc]的范围,它是从d开始的。

&emsp;
8. [^ 字符] 不匹配指定区间的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

$zz = '/[^0-9A-Za-z_]/';

$string = 'aaaaab311dd';

$string1 = '!$@!#%$#^##';

if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}

?>

结论:

  1. 匹配$string不成功,但是匹配$string1的时候成功。因为中括号里面有个抑扬符。
  2. ^ 抑扬符在中括号里面的作用是不准以中括号里面的字符进行匹配。

&emsp;

5. 元字符

抛出问题:\d 代表匹配一个字符。而我现在想要匹配十个八个,任意多个数字肿么办?
这个时候我们就要用到元字符。在使用原子的时候,发现只能够匹配一个字符,可是要匹配多个字符就出现了问题。
这个时候,我们需要借助元字符来帮我们修饰原子,实现更多的功能。

元字符 功能说明
  • | 是代表匹配前面的一个原子,匹配0次或者任意多次前面的字符。
  • | 匹配一次或多前前面的一个字符
    ? | 前面的字符可有可无【可选】 有或没有
    . | 更标准一些应该把点算作原子。匹配除了\n以外的所有字符
    丨 | 或者。注:它的优先级最低了。
    ^ | 必须要以抑扬符之后的字符串开始
    $ | 必须要以$之前的字符结尾
    \b | 词边界
    \B | 非边界
    {m} | 有且只能出现m次
    {n,m}| 可以出现n到m次
    {m,} | 至少m次,最大次数不限制
    () | 改变优先级或者将某个字符串视为一个整体,匹配到的数据取出来也可以使用它

&emsp;
1. + 匹配最少1次前面的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$zz = '/\d+/';

$string = "迪奥和奥迪250都是我最爱";

//待会儿再试试中间没有0-9的情况
//$string = "迪奥和奥迪都是我最爱";


if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

运行结果:
匹配成功,证明了\d+中的+。\d是匹配数字,而+是最少匹配一次前面的字符。

&emsp;
2. * 匹配0次或者任意多次前面的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$zz = '/\w*/';

$string = "!@!@!!@#@!$@#!";

//待会儿再试试中间没有0-9的情况
//$string1 = "!@#!@#!abcABC#@#!";


if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

说明,注释掉的$string1和$string都匹配成功了。因为,\w是匹配的0-9A-Za-z_,而*是说明前面的\w可以不存在。如果存在可以有1个或者多个。

&emsp;
3. ? 前面的字符出现0次或者1次,可有可无

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

$zz = '/ABC\d?ABC/';

$string = "ABC1ABC";

//待会儿再试试中间没有0-9的情况
//$string1 = "ABC888888ABC";
//$string2 = "ABCABC";


if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

匹配$string,$string2成功,但是匹配$string1失败。
因为匹配前后都是ABC,中间是一个0-9。0-9可有可无,但是不能有多个。

&emsp;
4. . (点) 匹配除\n以外的所有字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

$zz = '/gg.+gg/';

$string = "ABC1ABC";


if(preg_match($zz, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches);
}else{
echo '没有匹配到';
}
?>

匹配$string,$string2成功,但是匹配$string1失败。
因为匹配前后都是ABC,中间是一个0-9。0-9可有可无,但是不能有多个。

&emsp;
5. |(竖线),或者,优先级最低

我们通过实验来看优先级和或者的匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

$zz = '/abc|bcd/';

$string1 = "abccd";
$string2 = "ggggbcd";

if (preg_match($zz, $string1, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

我们来看看:
1.最开始我匹配的想法是想匹配的是abccd或者是abbcd。可是,匹配$string1和$string2,匹配出来的结果却是abc和bcd.
2.实现了或者匹配,匹配出来了abc或者是bcd。它还没有字符串连续在一起的优先级高。

那么问题来了,我要匹配上例中的abccd或者是abbcd怎么办?
需要使用到**()**来改变优先级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

$zz = '/ab(c|b)cd/';

$string1 = "起来abccd阅兵";
$string2 = "ggggbcd";
$string3 = '中国abbcd未来';

if (preg_match($zz, $string1, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

运行结果:
匹配到了,结果为:array(2) { [0]=> string(5) "abccd" [1]=> string(1) "c" }

结论:
1.确实匹配了了abccd或者abbcd($string1 or $string3)。
2.但是匹配的数组里面多了一个元素,这个元素的下标为1
3.()中的内容只要匹配成功,会把匹配到的数据放到下标为1的这个数组元素中。

&emsp;
6. ^ (抑扬符),必须要以^之后的字符串开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

$zz = '/^猪哥好帅\w+/';

$string1 = "猪哥好帅abccdaaaasds";
//$string2没有以猪哥好帅开始
$string2 = "帅abccdaaaasds";


if (preg_match($zz, $string1, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

通过实验发现以下结论:
1.$string1匹配成功,$string2没有匹配成功
2.因为$string1是以指定的字符开始的
3.而$string2并没有以^之后的字符开始
4.翻译这个正则的意思就是:以要猪哥好帅开始后面接a-zA-Z0-9_最少一个字符。

&emsp;
7. $ (美元符)必须要以$之前的字符结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

$zz = '/\d+努力$/';

$string1 = "12321124333努力";
//$string2
$string2 = "12311124112313力";


if (preg_match($zz, $string1, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

我们运行一下看一下结果,得出来的结论:
1.$string1 匹配成功,而$string2匹配不成功
2.$之前的字符是\d+,后面接着中文的努力。
3.因此,匹配的是这一个整体。\d指的是0-9的整型,+号代表最少一个0-9

&emsp;
8. \b和\B 词边界和非词边界

我们来讲解什么是边界:

1.正则表达示是有边界的,这个边界是定界符的开始和结尾是正则的边界。

2.this是一个英文单词,后面加上一个空格,意味着这个词结束了,到达了这个词的边界

\b词边界,就是指必须要在最前或者最后。
\B非边界,就是不能在一个正则表达示的最前或者最后。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

$zz = '/\w+\b/';

$string1 = "this is a apple";
$string2 = "thisis a apple";
$string3 = "thisisaapple";

if (preg_match($zz, $string1, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

结论:
1.$string1、$string2和$string3都匹配成功。
2.$string1匹配的时候this 空格是边界
3.$string2匹配的时候thisis是边界
4.$string3匹配的时候,thisisaapple到了整个正则表达示的最后,因此也是边界。所以匹配成功。

我们来实验一下非词边界:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

$zz = '/\Bthis/';

$string1 = "hellothis9";

//$string2 = "hello this9";
//$string2 = "this9中国万岁";

if (preg_match($zz, $string1, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

总结:
1.匹配$string1成功而$string2不成功。
2.因为\B后接的是this,所以this不能在词边界(空格和开始结尾)的位置出现。

&emsp;
9. {m}有且只能出现m次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

$zz = '/喝\d{3}酒/';

$string1 = "喝988酒";

//$string2 = "喝98811酒";

if (preg_match($zz, $string1, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

结论:
上例中\d{3}我规定了0-9只能出现3次,多一次少一次都不行。

&emsp;
10. {n,m} 可以出现n到m次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

$zz = '/喝\d{1,3}酒/';

$string1 = "喝9酒";

//$string2 = "喝988酒";

if (preg_match($zz, $string1, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

结论:
上例中\d{1,3}我规定了0-9只能出现1次,2次或者3次。其它次数都是错的

&emsp;
11. {m,} 至少m次,最大次数不限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

$zz = '/喝\d{2,}/';

$string1 = "喝9";

//$string2 = "喝98";
//$string3 = "喝98122121";


if (preg_match($zz, $string1, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

结论:
上例中\d{2,}我规定喝后面的0-9最少出现两次,最多次数不限。因此$string1是匹配不成功的,$string2是匹配成功的。$string3是匹配成功的。

&emsp;

6. 模式修正符

我们通过元字符和原子完成了正则表达示的入门。有一些特殊情况我们依然需要来处理。
如果abc在第二行的开始处如何匹配?
我不希望正则表达示特别贪婪的匹配全部,只匹配一部份怎么办?
这个时候,我们就需要用到下面的这些模式匹配来增强正则的功能。

常用的模式匹配符有:

模式匹配符 功能
i 模式中的字符将同时匹配大小写字母.
m 字符串视为多行
s 将字符串视为单行,换行符作为普通字符.
x 将模式中的空白忽略.
A 强制仅从目标字符串的开头开始匹配.
D 模式中的美元元字符仅匹配目标字符串的结尾.
U 匹配最近的字符串.

模式匹配符的用法如下:/ 正则表达示/模式匹配符 &emsp; 模式匹配符是放在正则表达式的最后的。
例如: /\w+/s

&emsp;
1. i 不区分大小写

1
2
3
4
5
6
7
8
9
10
<?php //在后面加上了一个i 
$pattern = '/ABC/i';
$string = '8988abc12313';
$string1 = '11111ABC2222';
if(preg_match($pattern, $string, $matches)){
echo '匹配到了,结果为:';
var_dump($matches); }else{
echo '没有匹配到';
}
?>

结论,不论是$string还是$string1全都匹配成功了。因此,在后面加上了i之后,能够不共分匹配内容的大小写。

&emsp;
2. m 视为多行
正则在匹配的时候,要匹配的目标字符串我们通常视为一行。
“行起始”元字符(^)仅仅匹配字符串的起始,“行结束”元字符($)仅仅匹配字符串的结束。
当设定了此修正符,“行起始”和“行结束”除了匹配整个字符串开头和结束外,还分别匹配其中的换行符的之后和之前。
注意:如果要匹配的字符串中没有“\n”字符或者模式中没有 ^ 或 $,则设定此修正符没有任何效果。

我们通过实验和代码来验证一下这个特点:
第一次匹配,你会发现匹配不成功:(1)↓↓

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$pattern = '/^a\d+/';
$string = "我的未来在自己手中我需要不断的努力
a9是一个不错的字符表示
怎么办呢,其实需要不断奋进";
if (preg_match($pattern, $string, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

&emsp;
第二次匹配,我们加上m 试试:(2)↓↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$pattern = '/^a\d+/m';
$string = "我的未来在自己手中我需要不断的努力
a9是一个不错的字符表示
怎么办呢,其实需要不断奋进";
if (preg_match($pattern, $string, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

运行结果:
匹配到了,结果为:array(1) { [0]=> string(2) "a9" }

哦耶!匹配成功了。/^a\d+/ 匹配的内容是a9,必须得在行开始处。在第二行也被匹配成功了。

&emsp;
3. s 视为一行
如果设定了此修正符,模式中的圆点元字符(.)匹配所有的字符,包括换行符。

第一次,不加模式匹配符s:(1)↓↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

$pattern = '/新的未来.+\d+/';

$string = '新的未来
987654321';

if (preg_match($pattern, $string, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

&emsp;
第二次,在正则表达示后面加上模式匹配符s:(2)↓↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

$pattern = '/新的未来.+\d+/s';

$string = "新的未来
987654321";

if (preg_match($pattern, $string, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

运行结果:
匹配到了,结果为:array(1) { [0]=> string(22) "新的未来 987654321" }

结论:

  1. 因为在新的未来,未来后面有一个换行
  2. 而.(点)是匹配非空白字符以外的所有字符。因此,第一次不成功
  3. 第二次,加上了s模式匹配符。因为,加上后.(点)能匹配所有字符。

&emsp;
4. x 忽略空白字符

  1. 如果设定了此修正符,模式中的空白字符除了被转义的或在字符类中的以外完全被忽略。
  2. 未转义的字符类外部的#字符和下一个换行符之间的字符也被忽略。
  • (1)↓↓我们先来实验一下忽略空白行等特性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

$pattern = '/a b c /x';

$string = '学英语要从abc开始';

if (preg_match($pattern, $string, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

运行结果:
匹配到了,结果为:array(1) { [0]=> string(3) "abc" }

这样也能匹配成功。
在$pattern里面有空格,每个abc后面有一个空格。而$string里面没有空格。
所以x忽略空白字符。

  • (2)↓↓而第二句话从字面上比较难理解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
//重点观察这一行
$pattern = '/a b c #我来写一个注释
/x';

$string = '学英语要从abc开始';

if (preg_match($pattern, $string, $matches)) {
echo '匹配到了,结果为:';
var_dump($matches);
} else {
echo '没有匹配到';
}
?>

运行结果:
匹配到了,结果为:array(1) { [0]=> string(3) "abc" }

结果也匹配成功了!
我们发现,x的第二个特性是忽略:#字符和下一个换行符之间的字符也被忽略

&emsp;
5. e 将匹配项找出来,进行替换
e模式也叫逆向引用。主要的功能是将正则表达式括号里的内容取出来,放到替换项里面替换原字符串。
使用这个模式匹配符前必须要使用到preg_replace()
mixed preg_replace ( mixed $正则表达式 , mixed $用于取代字符串的字串 , mixed $目标字符串)
preg_replace的功能:使用正则表达式 ,找到目标字符串。然后用’用于取代字符串的字串’进行替换。

在正式讲解前我们回顾一下之前的知识,我们故意的把每个要匹配的原子外面都加上括号:↓↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
//加上了括号
$pattern = '/(\d+)([a-z]+)(\d+)/';

$string = '987abc321';

if (preg_match($pattern, $string, $match)) {
echo '匹配到了,结果为:';
var_dump($match);

} else {
echo '没有匹配到';
}
?>

运行结果:
匹配到了,结果为:array(4) { [0]=> string(9) "987abc321" [1]=> string(3) "987" [2]=> string(3) "abc" [3]=> string(3) "321" }

这是我们之前讲括号的时候:匹配到的内容外面有括号。会把括号里面的内容,也放到数组的元素里面。如图中的:987、abc、321。

&emsp;
我们接下来看正则表达示中的e模式:↓↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$string = "{April 15, 2003}";

//'w'匹配字母,数字和下划线,'d'匹配0-99数字,'+'元字符规定其前导字符必须在目标对象中连续出现一次或多次
$pattern = "/{(\w+) (\d+), (\d+)}/i";

$replacement = "\$2";

//字符串被替换为与第 n 个被捕获的括号内的子模式所匹配的文本
echo preg_replace($pattern, $replacement, $string);

?>

运行结果:
15

结论:

  1. 上例中$2 指向的是正则表达示的第一个(\d+)。相当于把15又取出来了
  2. 替换的时候,我写上$2。将匹配项取出来,用来再次替换匹配的结果。

&emsp;
6. U 贪婪模式控制
正则表达式默认是贪婪的,也就是尽可能的最大限度匹配。

我们来看看正则表达示是如何贪婪的:↓↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$pattern = '/<div>.*<\/div>/';

$string = "<div>你好</div><div>我是</div>";

if (preg_match($pattern, $string, $match)) {
echo '匹配到了,结果为:';
var_dump($match);
} else {
echo '没有匹配到';
}

运行结果:
匹配到了,结果为:array(1) { [0]=> string(34) "
你好
我是
" }
?>

我们来看看结果,得到如下结论。它从“
你好”直接匹配到了“我是
”。进行了最大范围的匹配。

&emsp;
同样一段代码我们再加大写的U,再看看效果:↓↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$pattern = '/<div>.*<\/div>/U';

$string = "<div>你好</div><div>我是</div>";

if (preg_match($pattern, $string, $match)) {
echo '匹配到了,结果为:';
var_dump($match);
} else {
echo '没有匹配到';
}

?>

运行结果:
匹配到了,结果为:array(1) { [0]=> string(17) "
你好
" }

我们发现,只匹配出来了:<div>你好</div>
这样,把正则的贪婪特性取消掉。让它找到了最近的匹配,就OK了。

&emsp;
7. A 从目标字符串的开头开始匹配
此模式类似于元字符中的^(抑扬符)效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

$pattern = '/this/A';

$string = 'hello this is a ';
//$string1 = 'this is a ';

if (preg_match($pattern, $string, $match)) {
echo '匹配到了,结果为:';
var_dump($match);
} else {
echo '没有匹配到';
}
?>

结论:
1.如果加A模式修正符的时候匹配不出来$string,不加时能匹配出来
2.如果加上了A模式修正符的时候能匹配出来$string1,因为必须要从开始处开始匹配

&emsp;
8. D 结束$符后不准有回车
如果设定了此修正符,模式中的美元元字符仅匹配目标字符串的结尾。没有此选项时,如果最后一个字符是换行符的话,美元符号也会匹配此字符之前。

1
2
3
4
5
6
7
8
9
10
11
<?php 
$pattern = '/\w+this$/';
$pattern1 = '/\w+this$/D';
$string = "hellothis ";
if (preg_match($pattern1, $string, $match)) {
echo '匹配到了,结果为:';
var_dump($match);
}else {
echo '没有匹配到';
}
?>

结论:
1.如pattern 在匹配$string的时候,$string的字符串this后有一个回车。在没有加D匹配符的时候也能匹配成功
2.如pattern 在匹配$string的时候,加上了D。$string的字符串this后有空格,匹配不成功。

&emsp;

7. 写正则的诀窍和常用正则

写正则的诀窍
女孩和男孩恋爱时的诀窍通常是:测试你是不是对我好一点,如果是的,咱们的关系就更好一点。
而正则的诀窍和恋爱的诀窍基本一致:写一点、测一点。
因为,我们需要不断的正则,用preg_match对比是不是能匹配成功。成功了,再写后面的一点。直到写完,全部匹配成功为止!
例如,我要写一个邮箱的正则。我先要做的事情,将常用的邮箱格式全部列出来。例如:
phpcn@php.cn &emsp; phpcn@corp.baidu.cm &emsp; phpcn@126.com &emsp;phpcn@xxx.com &emsp;12345@qq.com

常用的格式主要有这样一些。那我们就可以来分析: 1.先匹配@之前的字符 \w+(因为是0-9A-Za-z_) 2.第二个跟一个@符 3.第三个再写上[a-zA-Z0-9-]+ 因为qq和126这些主域名是不能有下划线的 4.corp.baidu. 或者是126. 通常邮箱后缀都是这样的。所以我们可以写成:([a-zA-Z0-9-]+.){1,2} 5.上面的是将 **.** 转义,让它是本身的意思。括号重复的区间最少一次,最多两次。 6.后面接下com|cn|org|gov.cn|net|edu.cn等就可以了

因此,我们的正则表达式我使用:
/\w+@([a-zA-Z0-9-]+.){1,2}(com|cn|org|gov.cn|net|edu.cn)/

/\w+@([a-zA-Z0-9_]+)\.(com|cn|org|gov.cn|net|edu.cn)/
邮箱的正则就被我写成功了。ฅʕ•̫͡•ʔฅ

&emsp;

8. 常用正则函数

函数名 功能
preg_filter 执行一个正则表达式搜索和替换
preg_grep 返回匹配模式的数组条目
preg_match 执行一个正则表达式匹配
preg_match_all 执行一个全局正则表达式匹配
preg_replace_callback_array 传入数组,执行一个正则表达式搜索和替换使用回调
preg_replace_callback 执行一个正则表达式搜索并且使用一个回调进行替换
preg_replace 执行一个正则表达式的搜索和替换
preg_split 通过一个正则表达式分隔字符串

&emsp;
正则关于面试常遇到的问题
面试中经常考到的几个正则达达示是:

  1. 匹配邮箱
  2. 匹配手机号
  3. 匹配一个网址
  4. 用正则匹配某个格式,取出某个例
  5. 写一个采集器
    其他….

面试的第4题和第5题我对大家不担心,因为只要大家认真学习了我给的前五节的内容。第4,5题推理就行。
因为,通常在技术答题面试环节,是时候准许查手机的! ヾ(๑╹◡╹)ノ”