正则表达式入门
概念
字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是0个或更多个字符的序列。文本也就是文字,字符串。 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
支持
在最近的六十年中,正则表达式逐渐从模糊而深奥的数学概念,发展成为在计算机各类工具和软件包应用中的主要功能。不仅仅众多UNIX工具支持正则表达式,近二十年来,在WINDOWS的阵营下,正则表达式的思想和应用在大部分 Windows 开发者工具包中得到支持和嵌入应用!从正则式在Microsoft Visual Basic 6 或 Microsoft VBScript到.NET Framework中的探索和发展,WINDOWS系列产品对正则表达式的支持发展到无与伦比的高度,几乎所有 Microsoft 开发者和所有.NET语言都可以使用正则表达式。如果你是一位接触计算机语言的工作者,那么你会在主流操作系统(*nix[Linux, Unix等]、Windows、HP、BeOS等)、主流的开发语言(PHP、C#、Java、C++、VB、Javascript、Ruby以及python等)、数以亿万计的各种应用软件中,都可以看到正则表达式优美的舞姿。
以上内容引自百度百科
[TOC]
测试工具
在线版
http://regex.larsolavtorvik.com/ http://tool.oschina.net/regex http://www.rubular.com/ http://zhengze.51240.com/ http://www.kingshang.com/ http://zhengze.51240.com/
离线版
规则
通配符
还记得*
和?
通配符吗?
如果要找到所有pdf文件,就在文件管理器中输入*.pdf即可。一般的搜索通配符已经可以很好的对付了,但是如果需要搜索的条件突然变得很复杂:我需要在号码簿里筛选出来北京和陕西省
所有的手机号
和座机号
,通配符就表示压力山大了!而这项任务对于正则表达式而言,简直是轻而一举。我相信你看完此文,一定能轻松写出对应的匹配语句!
最基础
9527
10086
regex
这种最平常不过的字符所蕴含的意思就是他们本身
字符组
字符组就是在[]
(方括号)中列举出所有的可能再去匹配
直接匹配
[0-9]
匹配一个数字
[aeiou]
匹配任何一个英文元音字母
[.?!]
匹配标点符号(.或?或!)。
gr[ae]y
匹配grey 或者 gray
方括号内的多个字符实际上只占一个坑,他无法匹配greay或graay,因为
gr[ae]y
只匹配四个字母,[ae]
只占一个
[Hh][123456]
匹配HTML里所有的h标签,这种写法考虑到了H标签的大小写
PS.在w3c的规范里还是推荐所有html标签都必须是小写字母,所有属性都使用双引号包裹
排除型匹配
gr[^ae]y
匹配除了grey和gray以外的所有单词
h[^123]
匹配不是h1,h2,h3的标签
元字符
元字符就是在正则语言中代表特殊意义的字符,如
[0-9]
代表的含意与\d
一样
[a-z0-9A-Z_]
也完全等同于\w
(如果只考虑英文的话)。
^
代表每一行的开始,$
代表每一行的结束
^$
匹配空行
^foot$ 匹配只有foot一个词的行
元字符的出现可以理解为方便书写
基础元字符表
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\W | 匹配任意不是字母或数字或下划线或汉字的字符 |
\s | 匹配任意的空白符 |
\S | 匹配任意非空白符 |
\d | 匹配数字 |
\D | 匹配非数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
转义
如果要匹配 C:\\WINDOWS
,我们要如何描述\
反斜杠呢?
这个时候就需要用到转义,在这种特殊标点前面加一个\
,他的意思就表示后面的标点是普通的标点,比如\\w
匹配字符 \w,这个时候\w就不再表示一个字符了
字符组里面的内容不需要转义
重复
{n} n代表重复次数,前面一定是元字符或者分组 \d{11} 匹配一个11位的数字,如果要匹配手机号码,需要一些改造
代码 | 说明 |
---|---|
\* | 重复零次或更多次 |
\+ | 重复一次或更多次 |
\? | 重复零次或一次 |
{n} | 重复 n 次 |
{n,} | 重复 n 次或更多次 |
{n,m} | 重复 n 到 m 次 |
小测试:如何模糊匹配IP地址
重复只对紧邻的上一个最小正则单元起作用,如123*不能匹配123123,可以匹配12333
贪婪
贪婪顾名思义就是尽力的匹配,这也是正则表达式中默认的匹配模式,与此对用的就是另一种模式叫最小匹配,即在能匹配更多的情况下选择放弃,总是返回最小的结果集。
例子:我们现在想找到类似通配符下的a*c
下字符,即a开头c结尾的字符串。
abccccbcdda
我们这样写a\w*c
,和这样写a\w*?c
得到的结果是不一样的
表达式 | 结果 |
---|---|
a\w*c | abccccbc |
a\w*?c | abc |
在*
重复的情况下,后面的?
告诉重复符*
不要匹配太多,所以当找到第一个c的时候就收手了,而默认情况下匹配到了最后一个c。
所以在写*或{n,m}重复的时候一定要注意是否需要贪婪模式,否则匹配后的结果可能会略过很多可能你需要的信息。
选择分支
在此我们引入一个符号|
,他表示或,即程序语言里的or
。
以下引用自正则表达式30分钟入门教程
\d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。
括号的作用
界定作用范围
还记得gr[ae]y吗? gr(a|e)y
也可以实现这样的匹配,如果没有括号,gra|ey
就成了毫无关系的两部分。
分组和反向引用
正则表达式的匹配和捕获
正则表达式的匹配其实就是点到即止,只要符合表达式的规则即可,但是引入了分组以后,正则表达式就有了更大的发挥空间。
分组使用括号标记出本次匹配需要提取的数据,并且将匹配成功的数据返回给程序供其使用。
对于grey这个单词 gr[ae]y
和 gr(e|a)y
都可以将其匹配,但是对于前者,只能匹配grey,而后者将匹配的内容返回,即捕获了字母e。
PS.分组往往伴随着分隔符出现,但是请不要把二者的真正含义搞混了。
分组所提取出来的值可能不止一组,正则会把他们自动编号,从0(0表示所有匹配)开始,group1是第一个分组,以此类推。分组可以被捕获,以BBCODE为例,下面是源代码
正常文字
正常文字
我是 [b]粗体字[/b] Ctrl+B
我是粗体字
我是[i]斜体字[/i] Ctrl+I
我是斜体字
我是[u]下划线文字[/u] Ctrl+U
我是下划线文字
我是[s]删除线文字[/s] Ctrl+D
我是删除线文字
我是[mask]马赛克文字[/mask] Ctrl+M
我是马赛克文字
我是
[color=red]彩[/color][color=green]色[/color][color=blue]的[/color][color=orange]哟[/color]。
我是
彩色的哟。
[size=10]不同[/size][size=14]大小的[/size][size=18]文字[/size]效果也可实现。
不同大小的文字效果也可实现。
Bangumi 番组计划: [url]http://chii.in/[/url] Ctrl+L
Bangumi 番组计划: http://chii.in/
带文字说明的网站链接:
[url=http://chii.in]Bangumi 番组计划[/url] Ctrl+L
带文字说明的网站链接:
Bangumi 番组计划
存放于其他网络服务器的图片:
[img]http://chii.in/img/ico/bgm88-31.gif[/img]
首先,我们用\[([a-z]+)\](.*?)\[\/\1]
匹配,最后的\1
意思是第一个分组,用来闭合标签,但是发现只能得到前面几个简单的标签,因为我们没有考虑到有些标签是有属性的。
我们修改了刚才的表达式,再用\[([a-z]+)=?(\w*)\](.*?)\[\/\1]
去匹配这句话,可以得到标签、属性和内容。
下面我们来分析一下:([a-z]+)用于tag,注意等于号的出现次数,等号后面就是属性,(.*)
提取到了标签里的内容,最后引用第一分组使标签闭合!
分组别名
分组在创建时默认的命名为1,2,3,但是你可能为了方便想自己命名,这个功能正则早都考虑到了。只要在分组的前面或后面加入?<groupname>
,其中groupname就是你的组名,在后面引用的时候用\k<groupname>
引用即可。
所以刚才的表达式可以修改为:
\[(?<tag>[a-z]+)=?(\w*)\](.*?)\[\/\k<tag>]
分组别名可以让正则表达式更容易理解,而且在coding的时候别名会成为键名返回给程序员,简化了很多操作!
上面的正则放在PHP里运行,则会返回以下结果,自动保存了默认组名和别名。
//以上省略
[tag] => Array
(
[0] => b
[1] => i
[2] => u
[3] => s
[4] => mask
[5] => color
[6] => color
[7] => color
[8] => color
[9] => size
[10] => size
[11] => size
[12] => url
[13] => img
)
[1] => Array
(
[0] => b
[1] => i
[2] => u
[3] => s
[4] => mask
[5] => color
[6] => color
[7] => color
[8] => color
[9] => size
[10] => size
[11] => size
[12] => url
[13] => img
)
//以下省略
替换
正则表达式的匹配或者提取已经基本讲完了,下面
<?php
$string = 'April 15, 2003';
$pattern = '/(\w+) (\d+), (\d+)/i';
$replacement = '$1,16,$3';
echo preg_replace($pattern, $replacement, $string);
//April,16,2003
?>
上面的程序先用正则表达式提取出三个分组,分别匹配了月份,日期和年份。
再看变量replacement里的$1
、$3
,他们就代表了第一分组和第三分组
我们刚刚学习了分组命名,我们试试修改第二组的命名
<?php
$string = 'April 15, 2003';
$pattern = '/(\w+) (?<month>\d+), (\d+)/i';
$replacement = '$1,16,$2,$3,$month';
echo preg_replace($pattern, $replacement, $string);
//April,16,15,2003,$month
?>
结果好像不是我们想要的,看来分组命名在PHP的正则替换里没有作用,以后使用的时候一定要注意!
断言
断言的意思就是预先判断匹配字符的位置,以达到更精确的匹配。源文本如下:
tar rat head tail echo var_dump ma1ke
现在需要找出字母a前面是一个字母v或者r的词,我们使用(?<=[vr])a,即可达到效果。即在原本的条件左边附加(?<=expression)。
现在需要找出字母a后面是一个字母d或者是数字的词,我们使用a(?=(\d|d)),即可达到效果。即在原本的条件右边附加(?=expression)。
断言只是条件,帮你找到真正需要的字符串,本身并不会匹配!所以不用担心他会影响分组编号。
总结
正则表达式水很深,但的确很强大!简单一行规则就包含了十分复杂的逻辑和运算,确实快赶上一门程序语言了,如果你能够掌握他,那么他会极高的提高你的工作效率。
但正则表达式不是一朝一夕就能掌握的,更多的在于理解正则表达式里的精神和情怀,去包容他,放纵他,打碎他,然后创造它!