接触正则应该有三年不止了吧,我也不知道自己怎么就走过了畏惧正则的时期,而且在一个小圈子里面还成了正则强人。
今天就基于我对正则的理解,简单描述一下我眼里的正则,希望这篇正则表达式入门教程能够解除正则新人对于正则的畏惧感。
先概括一下,正则三段论:
定锚点,去噪点,取数据
一、入门:正则字符
关于正则字符,很多文章都会讲到,足足有一篇文章才能描述清楚,我这里就不多说,对于我,平时,常用的有:
1. .
匹配不包括换行的任意字符
在php的s修饰符(单行模式)下面可以匹配换行,如$pattern='#<div>(.*?)</div>#s';
就可以匹配div内容有换行的数据。
如果需要匹配包括换行的任意字符,可以使用[\s\S]
代替.
2. \s
空格、tab、换行
[\s\S]表示匹配任意字符,\S是\s的反义。
注意区分[\s\S]与.的区别。
3. *
匹配零个或更多个,即0~n
4. +
匹配一个或更多个,即至少一个,1~n
5. \
转义
一个特殊字符前加\
就表示转义,说明把它当普通字符用
6. []
单字符取一个,比如[abc]会匹配a或b或c
但是,如果[]
里面加上^
则会变成排除这个字符,如[^abc]
就表示不是a、不是b、也不是c
另外,在[]
里面可以使用-
表示一个范围,如[0-9]
表示从0到9,类似的还有[a-zA-Z]
,如果要包含-
字符,可以给它加上转义[\-]
。
关于[]
常见的错误用法是:[ab|bc]
用来表示ab
或bc
,实际上,它得到的结果是[abc|]
,即a或b或c或|
这4个字符(单字符)的任意一个。这里可以改成(ab|bc)
。
总结:[]里面的特殊符有五个:[]-\^
,其他字符都是普通字符,包括*.?
等。
说明:
* ^
在[^
的首位时候才有特殊意义
* [0-9
-
在不是首尾的时候有特殊意义
* \[ \]
因为占用[]
本身字符,所以有特殊意义
* \
本身是转义符,有特殊意义
7. ^
字符串开始
这里的^跟[]
里面用的^
是同一个字符,但是却不是一个意思,这里它表示整个字符串的开始,比如^www
表示以www
开头的字符串,注意区分,不在[]
里面的是开始符,在里面的排除
8. $
字符串结束
9. {1,3}
循环次数
[0-9]{1,3}
表示在0-9的范围里面循环1个、2个或者3个,可能结果有5、20、415等。
如果循环指定次数,如3次,则{3,3}
可以简写成{3}
。
如果刚好需要匹配字符{1}
,则正则需要给{
进行转义,得到\{1}
的正则。
如果{}
中间不是数字,则{}
本身不需要转义。
10. ?
有两个用法
(1) 匹配一个或零个
比如https?
匹配的https
(一个s)或者http(零个s)
(2)非贪婪模式
所谓非贪婪模式,就是匹配尽可能少的内容,比如,对于源字符串
<div>a</div><div>b</div>
使用<div>(.*?)</div>
会得到2个结果(注意:如果源字符串有换行,使用[\s\S]替换 . ):
<div>a</div>
和
<div>b</div>
因为,当遇到第一个</div>
,非贪婪模式就不会再往后找了。
而使用<div>(.*)</div>
(贪婪模式)则会得到整个字符串
<div>a</div><div>b</div>
,因为它会匹配所有字符直到后面再找不到</div>
。
更多关于?
的使用,可以参考《正则表达式匹配次数》
11. |
多个数据选一(常用于多字符)
前面提到[]
里面的字符有选一个字符功能,但是假如不是一个字符,比如:http|ftp|svn 就需要用|
分开,|
的作用域是一直往后直到遇到括号,比如,对于源字符串
http abc
ftp abc
svn abc
http|ftp|svn abc
匹配的结果是:
http
和
ftp
和
svn abc
想要匹配 http abc
和ftp abc
和svn abc
就要使用括号把前边的协议括起来,如(http|ftp|svn) abc
可以得到预期的结果。
12. ()
数据分界和取数据
上面例子(http|ftp|svn) abc
就是数据分界的例子,然后,匹配结果会得到一个[1]
的子集数据(数组下标1),这里就是子模式的概念,子模式也叫分组,利用子模式,可以得到想要取出来的数据。子模式1、2、3的计算方法为左括号的计数,从左到右,从1开始,比如:
(http|ftp|svn)://([^/]+)
,分组1得到的是(http|ftp|svn)
里面的数据,分组2得到([^/]+)
里面的数据,对于嵌套括号也是点左括号即可。在正则中有很多与括号结合的写法,你在数左括号的时候,一定要注意,非捕获组和环视的左括号都是不需要数的。
在使用子模式过程中,常见两种写法是:\1
和 $1
。
(1) \1
是在正则表达式本身中引用分组1的内容,如:
我们要匹配111
这样的连续出现3此的数字,我们可以写出正则:(\d)\1\1
,(\d)
匹配到第一个1
,后面再引用这个匹配内容,得到111
。
(2) $1
是在替换中调用分组的内容,如:
我们要替换链接参数name=Zjmainstay
为username=Zjmainstay
,我们可以使用正则name=([^&]+)
替换为username=$1
来实现,这里的$1就引用了分组1的结果Zjmainstay
,因此得到我们想要的结果。
13. (?:)
非捕获组
上面说到()
作为子模式可以得到它里面的数据,但是,有些时候,()
只是作为数据分界功能,并不需要取出来,这时候就要用到非捕获组的概念了。比如:(http|ftp|svn)://([^/]+)
只想得到域名,也就是[2]
,那么(http|ftp|svn)
就只是数据分界的功能,这里不需要捕获,因此使用非捕获组功能,(?:http|ftp|svn)
屏蔽这部分的数据获取,此时,(?:
这个左括号排除[1]
计数,也就是(?:http|ftp|svn)://([^/]+)
中的([^/]+)
变成[1]
了。
14. 分隔符
在一些语言中,你会发现正则第一个和最后一个字符是相同的,如:
/\d+/
这个/ /
在PHP中称为分隔符,正则表达式需要由分隔符闭合包裹。在PHP中,分隔符可以使任意非字母数字、非反斜线、非空白字符。这个概念很关键,它能帮助我们简化一些正则的书写,避免错误,如:
/<div>.*?</div>/
这个正则是错误的。
原因是</div>
的/
与分隔符相同,但是却没有做转义。
如下程序:
preg_match('/<div>.*?</div>/', '<div>abc</div>', $match);
PHP中会收到错误提示:Warning: preg_match(): Unknown modifier 'd' in regexTest.php on line 2
对于这种情况,有两种解决方案:
(1)/<div>.*?<\/div>/
(2)#<div>.*?</div>#
方案(1)是对正则内部的分隔符做转义,方案(2)是规避了本来的/
分隔符,使用#
作为分隔符,从而避免/
需要转义。
虽然很多情况下,都是看到前后一致的分隔符,但是,大家需要了解一下,[<div>.*?</div>]
这个表达式在PHP里也是合法的。(不提倡使用,仅了解!)
15. 模式修饰符
模式修饰符在许多程序语言中都支持的,比如最常见的是i
,不区分大小写,如javascript里的/[a-z0-9]/i,表示匹配字母数字,不区分大小写。
本人在写php正则时常用的模式修饰符主要有i
和s
,如:
$pattern = '#[a-z0-9]+#is';
模式修饰符s的作用主要是的.
能够匹配换行,在处理换行数据时,通常会用到。
在PHP中,模式修饰符有两种用法,一种是上面的,在分隔符后面的模式修饰符,它的作用范围是全局;另一种是在正则表达式中间的。
例如:
正则:/((?i)[A-Z]+)c/
测试字符:abcABC
匹配:abc
说明:局部(ab)的大小写被忽略了,(?i)的作用范围在分组1内
如果把正则改成:/([A-Z]+)c/i
,则匹配结果将是:abcABC
示例地址:PHP正则表达式中间的模式修饰符 (选择Version 1/2切换版本查看结果区别)
关于PHP模式修饰符的讲解,请查看PHP手册中的《PHP模式修饰符》。
关于常用字符的使用差不多到这里,还有更多的请参考正则表达式30分钟入门教程,这是我看过比较全面的正则入门资料。
二、 操作:定锚点
注:这里的锚点区分于正则原本关于锚点的定义,此处是确定的参照文本的意思,如a标签里的<a
每一个正则都是有针对性的,只有这样正则才有意义。因此,写正则之前,先观察你要解析的数据,找准唯一的锚点,比如,你要解析一个页面的title标签,得到title内容,那么这个title就是锚点。有时候,所要取的数据确实无法定位一个唯一的锚点,那么,你可以分解数据,先通过一个唯一锚点锁定你的数据块,取出来之后,再对这个数据块取数据即可。比如,有这么一段源字符串:
<div id="module_1">
<div class="content">
content 1
</div>
</div>
<div id="module_2">
<div class="content">
content 2
</div>
</div>
你直接通过class="content"来匹配数据的话很明显会得到两个,那么,你可以扩展它的数据域,先以id="module_1"作为锚点,获取整个
<div id="module_1">
<div class="content">
content 1
</div>
</div>
然后在对这个数据块的数据处理,得到class="contents"的内容即可。
因此,这里用到2个正则:
(1)<div id="module_1">(.*?)</div>\s*<div id="module_2">
(2)<div class="content">(.*?)</div>
当然,这个正则可以改进为:
<div id="module_1">\s*<div class="content">(.*?)</div>
注:为了更清晰查看,前面源码做了换行,匹配失败的朋友,可以修改 .*?
为 [\s\S]*?
修正正则。
总结:锚点,就是能唯一定位你数据的标识
三、 操作:去噪点
所谓去噪点,就是把无关的东西都当浮云,用通配符过掉它,只关心我们想要的数据,比如:
<meta content="text/html; charset=utf-8" http-equiv="content-type">
要从这里得到字符集utf-8,我们需要怎么做?
首先,定位锚点,有<meta
、charset=
和utf-8后面的"
,其他都是浮云~
因此得到正则:
<meta[^>]*?charset=([^"]+)"
即可,用子模式取数据[1]
就能得到utf-8
总结:关心的留下,不关心的都是浮云
四、 操作:取数据
关于取数据,上面一大篇下来大家应该有概念了,就是利用子模式来获取,这里不再赘述。
总结:子模式计数,数左括号从1开始,排除非捕获组的左括号
五、正则表达式高级教程
关于正则表达式的高级教程,请阅读《深入正则表达式应用 - 正则表达式高级教程》
最后,希望大家有一个愉悦的正则之旅,你一定会爱上她的,跟我一样。: )
附:
正则测试工具 RegexBuddy 4.1.0-正则测试工具.rar
正则表达式工具RegexBuddy使用教程 正则表达式工具RegexBuddy使用教程
Mac 正则表达式工具
Mac正则表达式测试工具
未经同意禁止转载!
转载请附带本文原文地址:我眼里的正则表达式入门教程,首发自 Zjmainstay学习笔记