正则表达式入门

概念

字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是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/

离线版

正则表达式测试器c#绿色版
正则表达式测试英文版
更多下载


规则

通配符

还记得*通配符吗?

如果要找到所有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*cabccccbc
a\w*?cabc

*重复的情况下,后面的?告诉重复符*不要匹配太多,所以当找到第一个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]ygr(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)。

断言只是条件,帮你找到真正需要的字符串,本身并不会匹配!所以不用担心他会影响分组编号。

总结

正则表达式水很深,但的确很强大!简单一行规则就包含了十分复杂的逻辑和运算,确实快赶上一门程序语言了,如果你能够掌握他,那么他会极高的提高你的工作效率。

但正则表达式不是一朝一夕就能掌握的,更多的在于理解正则表达式里的精神和情怀,去包容他,放纵他,打碎他,然后创造它!

标签: regular, expression, regex, abc

添加新评论