PHP-正则表达式
世界上最难懂的是道士的鬼符、医生的处方和程序员的正则表达示。(ง •_•)ง
 
 
 
 
正则表达示我们其实之前经常看到,它主要用在以下一些地方:
1.匹配邮箱、手机号码、验证码
2.替换敏感的关键词。例如:涉及政治和骂人的话
3.文章采集。
4.早期的表情替换技术,ubb文件编码、markdown编辑器替换等
5.以后自己写模板引擎也需要用到正则表达示
其他….
正则表达式是一种描述字符串结果的语法规则,是一个特定的格式化模式,可以匹配、替换、截取匹配的字符串。常用的语言基本上都有正则表达式,如JavaScript、java等。其实,只有了解一种语言的正则使用,其他语言的正则使用起来,就相对简单些。文本主要围绕解决下面问题展开。
1. 定界符
定界符,就是定一个边界,边界以内的就是正则表达示。
PHP的正则表达示定界符的规定如下:
定界符,不能用a-zA-Z0-9\ 其他的都可以用。必须成对出现,有开始就有结束。
我们来例几个例子:
例子 | 说明 |
---|---|
/中间写正则/ | 正确 |
$中间写正则$ | 正确 |
%中间写正则% | 正确 |
^中间写正则^ | 正确 |
@中间写正则@ | 正确 |
#中间写正则# | 正确 |
(中间写正则) | 错误 |
A中间写正则A | 错误 |
 
 
2. 转义字符
注:\ 是转义字符,如果在以后正则表达示里面需要匹配/,如下:
/ / /
这个时候真要匹配/ 的时候,需要把定界符里面的/ 用转义字符转义一下,写成下面的例子:
/ </font>/ /
如果你觉得麻烦,遇到这种需要转义的字符的时候可以把两个正斜线(/ /)定界,改为其他的定界符(# #)。
 
 
3. 原子
原子是正则表达示里面的最小单位,原子说白了就是需要匹配的内容。一个成立的正则表达示当中必须最少要有一个原子。
所有可见不可见的字符就是原子!
说明:我们见到的空格、回车、换行、0-9、A-Za-z、中文、标点符号、特殊符号全为原子。
在做原子的实例前我们先来讲解一个函数,preg_match:
int preg_match ( string $正则 , string $字符串 [, array &$结果] )
功能:根据$正则变量,匹配$字符串变量。如果存在则返回匹配的个数,把匹配到的结果放到$结果变量里。如果没有匹配到结果返回0。
注:上面是preg_match常用的主要几个参数。我在上面将另外几个参数没有列出来。因为,另外两个参数太不常用了。
我们来通过实验来证明:
(1)↓↓
1 | <?php |
因为我希望的是匹配a,而$string当是是不存在a的,所以不成功。
(2)↓↓
1 | <?php |
上面的字符串中s后存在wq,因此匹配成功。
接下来我们匹配一个空格试试:
(3)↓↓
1 | <?php |
因上,$string这个变量的w字符后存在一个空格。所以匹配成功,输出了字符串类型,长度为1。只不过我们的肉眼不可见,看不到这个字符串而已。
 
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以外的所有字符。上面已经说明的很清楚了,我们进行实验一步一步对这些进行学习。
 
1. \d匹配一个0-9
1 | <?php |
 
2. \D匹配一个非0-9的值
1 | <?php |
 
3. \w匹配一个a-zA-Z0-9_
1 | <?php |
 
4. \W匹配一个非a-zA-Z0-9_
1 | <?php |
 
5. \s 匹配所有空白字符\n \t \r 空格
1 | <?php |
 
6. \S 非空字符
1 | <?php |
 
7. [] 指定范围的原子
1 | <?php |
结论:
上例中0-5匹配$string失败,而$string1成功。因为,$string中的第一个数值为6,不在[0-5]的范围之内。
1 | <?php |
结论:$string和$string1都匹配成功。因为\w就是[a-zA-Z0-9_]
1 | <?php |
结论:$string、$string1、$string2匹配成功,而$string3不成功。因为$string3超过了[abc]的范围,它是从d开始的。
 
8. [^ 字符] 不匹配指定区间的字符
1 | <?php |
结论:
- 匹配$string不成功,但是匹配$string1的时候成功。因为中括号里面有个抑扬符。
- ^ 抑扬符在中括号里面的作用是不准以中括号里面的字符进行匹配。
 
5. 元字符
抛出问题:\d 代表匹配一个字符。而我现在想要匹配十个八个,任意多个数字肿么办?
这个时候我们就要用到元字符。在使用原子的时候,发现只能够匹配一个字符,可是要匹配多个字符就出现了问题。
这个时候,我们需要借助元字符来帮我们修饰原子,实现更多的功能。
元字符 | 功能说明 |
---|
- | 是代表匹配前面的一个原子,匹配0次或者任意多次前面的字符。
- | 匹配一次或多前前面的一个字符
? | 前面的字符可有可无【可选】 有或没有
. | 更标准一些应该把点算作原子。匹配除了\n以外的所有字符
丨 | 或者。注:它的优先级最低了。
^ | 必须要以抑扬符之后的字符串开始
$ | 必须要以$之前的字符结尾
\b | 词边界
\B | 非边界
{m} | 有且只能出现m次
{n,m}| 可以出现n到m次
{m,} | 至少m次,最大次数不限制
() | 改变优先级或者将某个字符串视为一个整体,匹配到的数据取出来也可以使用它
 
1. + 匹配最少1次前面的字符
1 | <?php |
 
2. * 匹配0次或者任意多次前面的字符
1 | <?php |
说明,注释掉的$string1和$string都匹配成功了。因为,\w是匹配的0-9A-Za-z_,而*是说明前面的\w可以不存在。如果存在可以有1个或者多个。
 
3. ? 前面的字符出现0次或者1次,可有可无
1 | <?php |
匹配$string,$string2成功,但是匹配$string1失败。
因为匹配前后都是ABC,中间是一个0-9。0-9可有可无,但是不能有多个。
 
4. . (点) 匹配除\n以外的所有字符
1 | <?php |
匹配$string,$string2成功,但是匹配$string1失败。
因为匹配前后都是ABC,中间是一个0-9。0-9可有可无,但是不能有多个。
 
5. |(竖线),或者,优先级最低
我们通过实验来看优先级和或者的匹配
1 | <?php |
我们来看看:
1.最开始我匹配的想法是想匹配的是abccd或者是abbcd。可是,匹配$string1和$string2,匹配出来的结果却是abc和bcd.
2.实现了或者匹配,匹配出来了abc或者是bcd。它还没有字符串连续在一起的优先级高。
那么问题来了,我要匹配上例中的abccd或者是abbcd怎么办?
需要使用到**()**来改变优先级。
1 | <?php |
结论:
1.确实匹配了了abccd或者abbcd($string1 or $string3)。
2.但是匹配的数组里面多了一个元素,这个元素的下标为1
3.()中的内容只要匹配成功,会把匹配到的数据放到下标为1的这个数组元素中。
 
6. ^ (抑扬符),必须要以^之后的字符串开始
1 | <?php |
通过实验发现以下结论:
1.$string1匹配成功,$string2没有匹配成功
2.因为$string1是以指定的字符开始的
3.而$string2并没有以^之后的字符开始
4.翻译这个正则的意思就是:以要猪哥好帅开始后面接a-zA-Z0-9_最少一个字符。
 
7. $ (美元符)必须要以$之前的字符结束
1 | <?php |
我们运行一下看一下结果,得出来的结论:
1.$string1 匹配成功,而$string2匹配不成功
2.$之前的字符是\d+,后面接着中文的努力。
3.因此,匹配的是这一个整体。\d指的是0-9的整型,+号代表最少一个0-9
 
8. \b和\B 词边界和非词边界
我们来讲解什么是边界:
1.正则表达示是有边界的,这个边界是定界符的开始和结尾是正则的边界。
2.this是一个英文单词,后面加上一个空格,意味着这个词结束了,到达了这个词的边界
\b词边界,就是指必须要在最前或者最后。
\B非边界,就是不能在一个正则表达示的最前或者最后。
1 | <?php |
结论:
1.$string1、$string2和$string3都匹配成功。
2.$string1匹配的时候this 空格是边界
3.$string2匹配的时候thisis是边界
4.$string3匹配的时候,thisisaapple到了整个正则表达示的最后,因此也是边界。所以匹配成功。
我们来实验一下非词边界:
1 | <?php |
总结:
1.匹配$string1成功而$string2不成功。
2.因为\B后接的是this,所以this不能在词边界(空格和开始结尾)的位置出现。
 
9. {m}有且只能出现m次
1 | <?php |
结论:
上例中\d{3}我规定了0-9只能出现3次,多一次少一次都不行。
 
10. {n,m} 可以出现n到m次
1 | <?php |
结论:
上例中\d{1,3}我规定了0-9只能出现1次,2次或者3次。其它次数都是错的
 
11. {m,} 至少m次,最大次数不限制
1 | <?php |
结论:
上例中\d{2,}我规定喝后面的0-9最少出现两次,最多次数不限。因此$string1是匹配不成功的,$string2是匹配成功的。$string3是匹配成功的。
 
6. 模式修正符
我们通过元字符和原子完成了正则表达示的入门。有一些特殊情况我们依然需要来处理。
如果abc在第二行的开始处如何匹配?
我不希望正则表达示特别贪婪的匹配全部,只匹配一部份怎么办?
这个时候,我们就需要用到下面的这些模式匹配来增强正则的功能。
常用的模式匹配符有:
模式匹配符 | 功能 |
---|---|
i | 模式中的字符将同时匹配大小写字母. |
m | 字符串视为多行 |
s | 将字符串视为单行,换行符作为普通字符. |
x | 将模式中的空白忽略. |
A | 强制仅从目标字符串的开头开始匹配. |
D | 模式中的美元元字符仅匹配目标字符串的结尾. |
U | 匹配最近的字符串. |
模式匹配符的用法如下:/ 正则表达示/模式匹配符   模式匹配符是放在正则表达式的最后的。
例如: /\w+/s
 
1. i 不区分大小写
1 | <?php //在后面加上了一个i |
结论,不论是$string还是$string1全都匹配成功了。因此,在后面加上了i之后,能够不共分匹配内容的大小写。
 
2. m 视为多行
正则在匹配的时候,要匹配的目标字符串我们通常视为一行。
“行起始”元字符(^)仅仅匹配字符串的起始,“行结束”元字符($)仅仅匹配字符串的结束。
当设定了此修正符,“行起始”和“行结束”除了匹配整个字符串开头和结束外,还分别匹配其中的换行符的之后和之前。
注意:如果要匹配的字符串中没有“\n”字符或者模式中没有 ^ 或 $,则设定此修正符没有任何效果。
我们通过实验和代码来验证一下这个特点:
第一次匹配,你会发现匹配不成功:(1)↓↓
1 | <?php |
 
第二次匹配,我们加上m 试试:(2)↓↓
1 | <?php |
哦耶!匹配成功了。/^a\d+/ 匹配的内容是a9,必须得在行开始处。在第二行也被匹配成功了。
 
3. s 视为一行
如果设定了此修正符,模式中的圆点元字符(.)匹配所有的字符,包括换行符。
第一次,不加模式匹配符s:(1)↓↓
1 | <?php |
 
第二次,在正则表达示后面加上模式匹配符s:(2)↓↓
1 | <?php |
结论:
- 因为在新的未来,未来后面有一个换行
- 而.(点)是匹配非空白字符以外的所有字符。因此,第一次不成功
- 第二次,加上了s模式匹配符。因为,加上后.(点)能匹配所有字符。
 
4. x 忽略空白字符
- 如果设定了此修正符,模式中的空白字符除了被转义的或在字符类中的以外完全被忽略。
- 未转义的字符类外部的#字符和下一个换行符之间的字符也被忽略。
- (1)↓↓我们先来实验一下忽略空白行等特性:
1 | <?php |
这样也能匹配成功。
在$pattern里面有空格,每个abc后面有一个空格。而$string里面没有空格。
所以x忽略空白字符。
- (2)↓↓而第二句话从字面上比较难理解:
1 | <?php |
结果也匹配成功了!
我们发现,x的第二个特性是忽略:#字符和下一个换行符之间的字符也被忽略。
 
5. e 将匹配项找出来,进行替换
e模式也叫逆向引用。主要的功能是将正则表达式括号里的内容取出来,放到替换项里面替换原字符串。
使用这个模式匹配符前必须要使用到preg_replace()。
mixed preg_replace ( mixed $正则表达式 , mixed $用于取代字符串的字串 , mixed $目标字符串)
preg_replace的功能:使用正则表达式 ,找到目标字符串。然后用’用于取代字符串的字串’进行替换。
在正式讲解前我们回顾一下之前的知识,我们故意的把每个要匹配的原子外面都加上括号:↓↓
1 | <?php |
这是我们之前讲括号的时候:匹配到的内容外面有括号。会把括号里面的内容,也放到数组的元素里面。如图中的:987、abc、321。
 
我们接下来看正则表达示中的e模式:↓↓
1 | <?php |
结论:
- 上例中$2 指向的是正则表达示的第一个(\d+)。相当于把15又取出来了
- 替换的时候,我写上$2。将匹配项取出来,用来再次替换匹配的结果。
 
6. U 贪婪模式控制
正则表达式默认是贪婪的,也就是尽可能的最大限度匹配。
我们来看看正则表达示是如何贪婪的:↓↓
1 | <?php |
我们来看看结果,得到如下结论。它从“
你好”直接匹配到了“我是
”。进行了最大范围的匹配。
 
同样一段代码我们再加大写的U,再看看效果:↓↓
1 | <?php |
我们发现,只匹配出来了:<div>你好</div>
这样,把正则的贪婪特性取消掉。让它找到了最近的匹配,就OK了。
 
7. A 从目标字符串的开头开始匹配
此模式类似于元字符中的^(抑扬符)效果。
1 | <?php |
结论:
1.如果加A模式修正符的时候匹配不出来$string,不加时能匹配出来
2.如果加上了A模式修正符的时候能匹配出来$string1,因为必须要从开始处开始匹配
 
8. D 结束$符后不准有回车
如果设定了此修正符,模式中的美元元字符仅匹配目标字符串的结尾。没有此选项时,如果最后一个字符是换行符的话,美元符号也会匹配此字符之前。
1 | <?php |
结论:
1.如pattern 在匹配$string的时候,$string的字符串this后有一个回车。在没有加D匹配符的时候也能匹配成功
2.如pattern 在匹配$string的时候,加上了D。$string的字符串this后有空格,匹配不成功。
 
7. 写正则的诀窍和常用正则
写正则的诀窍
女孩和男孩恋爱时的诀窍通常是:测试你是不是对我好一点,如果是的,咱们的关系就更好一点。
而正则的诀窍和恋爱的诀窍基本一致:写一点、测一点。
因为,我们需要不断的正则,用preg_match对比是不是能匹配成功。成功了,再写后面的一点。直到写完,全部匹配成功为止!
例如,我要写一个邮箱的正则。我先要做的事情,将常用的邮箱格式全部列出来。例如:
phpcn@php.cn   phpcn@corp.baidu.cm   phpcn@126.com  phpcn@xxx.com  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)/
邮箱的正则就被我写成功了。ฅʕ•̫͡•ʔฅ
 
8. 常用正则函数
函数名 | 功能 |
---|---|
preg_filter | 执行一个正则表达式搜索和替换 |
preg_grep | 返回匹配模式的数组条目 |
preg_match | 执行一个正则表达式匹配 |
preg_match_all | 执行一个全局正则表达式匹配 |
preg_replace_callback_array | 传入数组,执行一个正则表达式搜索和替换使用回调 |
preg_replace_callback | 执行一个正则表达式搜索并且使用一个回调进行替换 |
preg_replace | 执行一个正则表达式的搜索和替换 |
preg_split | 通过一个正则表达式分隔字符串 |
 
正则关于面试常遇到的问题
面试中经常考到的几个正则达达示是:
- 匹配邮箱
- 匹配手机号
- 匹配一个网址
- 用正则匹配某个格式,取出某个例
- 写一个采集器
其他….
面试的第4题和第5题我对大家不担心,因为只要大家认真学习了我给的前五节的内容。第4,5题推理就行。
因为,通常在技术答题面试环节,是时候准许查手机的! ヾ(๑╹◡╹)ノ”