首页 > 8 其它知识 > 正则表达式学习

正则表达式学习

2010年5月20日 AEROFISH 2,016 views 发表评论 阅读评论

正则表达式简单来讲就是用来筛选文本数据用的公式。几乎所有的程序设计语言都会支持正则表达式检索,而软件都是由程序编写的,因此只要程序作者原意,都是可以让做出的软件支持正则表达式的。如果你看到某款软件说明可以用正则表达式,那这篇文章就有用了。

1.0最简单的正则表达式

假设你要搜索一个包含字符"cat"的字符串,搜索用的正则表达式就是"cat"。如果搜索对大小写不敏感,单词"catalog"、"Catherine"、"sophisticated"都可以匹配。

1.1 句点符号

假设你在玩英文拼字游戏,想要找出三个字母的单词,而且这些单词必须以"t"字母开头,以"n"字母结束。另外,假设有一本英文字典,你可以用正则表达式搜索它的全部内容。要构造出这个正则表达式,你可以使用一个通配符——句点符号"."——来表达任一单个字符。这样,完整的表达式就是"t.n",它匹配"tan"、"ten"、"tin"和"ton",还匹配"t#n"、"t_n"甚至"t n",还有其他许多无意义的组合。这是因为句点符号匹配所有字符,包括空格、Tab字符甚至换行符。

1.2 方括号符号

为了解决句点符号匹配范围过于广泛这一问题,你可以在方括号"[]"里面指定看来有意义的单个字符。此时,只有方括号里面指定的字符才参与匹配。也就是说,正则表达式"t[aeio]n"只匹配"tan"、"Ten"、"tin"和"ton"。但"Toon"不匹配,因为在方括号之内你只能匹配单个字符,"Tun"也不匹配,因为方括号中没有包含"u"这个字符。

1.3 "或"符号

  如果除了上面匹配的所有单词之外,你还想要匹配"Toon",那么,你可以使用"|"操作符。"|"操作符的基本意义就是“或”运算。要匹配"toon",使用"t(a|e|i|o|oo)n"正则表达式。这里不能使用方扩号,因为方括号只允许匹配单个字符,这里必须使用圆括号"()"。圆括号还可以用来分组,具体请参见后面1.7.4介绍。

1.4 表示匹配次数的限定符

表一显示了表示匹配次数的限定符,这些符号用来确定紧靠该符号左边的符号出现的次数:

表一.表示匹配次数的限定符
符号 重复次数
* 0次或更多次
+ 1次或更多次
? 0次或1次
{n} 恰好n次
{n,} n次或更多次
{n,m} n到m次

(注:某些程序语言中可能对"+"不支持。)

假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如图一所示。在正则表达式中,连字符"-"有着特殊的意义,它表示一个范围,比如从0到9。因此,匹配社会安全号码中的连字符号时,它的前面要加上一个转义字符"\"。

图一:匹配所有123-12-1234形式的社会安全号码

假设进行搜索的时候,你希望连字符号可以出现,也可以不出现,即999-99-9999和999999999都属于正确的格式。这时,你可以在连字符号后面加上"?"数量限定符号,如图二所示:

图二:匹配所有123-12-1234和123121234形式的社会安全号码

下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分"[0-9]{4}",再加上字母部分"[A-Z]{2}"。图三显示了完整的正则表达式。

图三:匹配典型的美国汽车牌照号码,如8836KV

1.5 "否"符号

"^"符号用在方括号中称为"否"符号,表示不想要匹配的字符。例如,图四的正则表达式匹配所有单词,但以"X"字母开头的单词除外。如果不在方括号中为另外意思,详见后面1.7.2介绍。

图四:匹配所有单词,但“X”开头的除外

1.6 圆括号和空白符号

假设要从格式为"June 26, 1951"的生日日期中提取出月份部分,用来匹配该日期的正则表达式可以如图五所示:

图五:匹配所有Moth DD,YYYY格式的日期

新出现的"\s"符号是空白符号,匹配所有的空白字符,包括Tab字符。如果字符串正确匹配,接下来如何提取出月份部分呢?只需在月份周围加上一个圆括号创建一个组,然后用程序语言就能从分组中提取出它的值。修改后的正则表达式如图六所示:

图六:匹配所有Month DD,YYYY格式的日期,定义月份值为第一个组

1.7 其它符号

表二是其它一些重要的符号(包括之前介绍和未介绍的):

表二:正则表达式符号含义
符号 含义 近似或等价的正则表达式
[x] 匹配x单个字符  
[xyz] 匹配xyz这三个字母中的一个  
[^x] 匹配除了x以外的任意单个字符  
[^xyz] 匹配除了xyz这三个字母以外的任意单个字符  
\d 匹配单个数字 [0-9]
\D 匹配单个非数字 [^0-9]
\w 匹配单个字母、数字、下划线或汉字 [a-z0-9]
\W 匹配单个不是字母、数字、下划线或汉字 [^a-z0-9]
\s 匹配单个空白符(包括空格,制表符(Tab),换行符,中文全角空格等) [\t\n\r\f]
\S 匹配单个非空白符 [^\t\n\r\f]
\b 匹配单词的开始或结束  
[\b] 在方括号中为退格符 \u0008
^ 匹配字符串的开始或行首(在方括号外)  
$ 匹配字符串的结束或行尾(在方括号外)  
. 匹配任意单个字符(有时不包括换行符"\n")  
\. 句点本身  
\* 星号本身 \x2A
\\ 反斜杠本身  
\a 响铃报警字符(打印它的效果是电脑嘀一声) \u0007
\t 制表符,Tab \u0009
\r 回车 \u000D
\v 竖向制表符 \u000B
\f 换页符 \u000C
\n 换行符 \u000A
\e Escape \u001B
\0nn ASCII代码中八进制代码为nn的字符  
\xnn ASCII代码中十六进制代码为nn的字符  
\unnnn Unicode代码中十六进制代码为nnnn的字符  
\cN ASCII控制字符。比如\cC代表Ctrl+C  
\A 字符串开头(类似^,但不受处理多行选项的影响)  
\Z 字符串结尾(类似$,但不受处理多行选项的影响)  
\G 当前搜索的开头  
\p{name} Unicode中命名为name的字符类,例如\p{IsGreek}  
* 对前面的符号重复0次或更多次  
+ 对前面的符号重复1次或更多次(有时不支持)  
? 对前面的符号重复0次或1次  
{n} 对前面的符号恰好重复n次  
{n,} 对前面的符号重复n次或更多次  
{n,m} 对前面的符号重复n次到m次  
*? 对前面的符号重复任意次,但尽可能少重复  
+? 对前面的符号重复1次或更多次,但尽可能少重复  
?? 对前面的符号重复0次或1次,但尽可能少重复  
{n,}? 对前面的符号重复n次以上,但尽可能少重复  
{n,m}? 对前面的符号重复n到m次,但尽可能少重复  
(exp) 自动命名的分组,exp为子正则表达式(同下),一般按顺序命名为1~9  
(?<name>exp) 以name命名的分组,也可以写成(?'name'exp)  
(?:exp) 无命名的分组,无法被引用  
(?=exp) 匹配exp前面的内容,不包括exp  
(?<=exp) 匹配exp后面的内容,不包括exp  
(?!exp) 匹配后面跟的不是exp的内容  
(?<!exp) 匹配前面不是exp的内容  
(?#comment) 注释作用  

下面是对上表的一些说明和举例:

1.7.0"\d"

前面1.4中社会安全号码的例子,所有出现"[0-9]"的地方我们可以使用"\d"代替。修改后的正则表达式如图七所示:

图七:匹配所有123-12-1234格式的社会安全号码

另一个IP地址的匹配例子,图八:

图八:匹配IP地址

1.7.1"\b"

表示匹配单词的开始或结束

\ba\w*\b匹配以字母a开头的单词;\b\w{6}\b 匹配刚好6个字符的单词。

但如在"[]"内为退格符。

1.7.2"^"和"$"

表示匹配字符串开始和结束,同"\b"差不多。

^\d{5,12}$匹配5位到12位数字。

另外可以通过程序命令使其变成行首行尾匹配。

"^"符号用在方括号中称为"否"符号,详见之前1.5介绍。

1.7.3"\"转义符

当需要匹配" . $ ^ { [ ( | ) * + ? \"这些字符本身时,前面需要加转义符"\",其它字符不用。但一般情况下,为了记忆方便"] } /"之前也会加上转义符。如图九:

图九:匹配以"["开头和"]"结束的字符

1.7.4"()"小括号

小括号用来指定子表达式(也叫做分组)。"()" 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一般可以保存9个分组),它们可以用 \1 到\9 的符号来引用。

(\d{1,3}\.){3}\d{1,3}匹配如999.999.999.999这种格式,可以用来匹配IP地址,这个和1.7.0图八的例子是等效的。

默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。然后可用 \1 到\9 的符号来引用,注意引用的是以匹配好的分组内容。

\b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者hello hello。这个表达式首先是匹配一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字"\b(\w+)\b",这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符"\s+",最后是分组1中捕获的内容(也就是前面匹配的那个单词)"\1"。

1.7.5自定义分组名称

你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<name>exp),其中尖括号可换成单引号(?'name'exp),exp为子表达式,可以使用\k<name>来引用。1.7.5的例子也可以写成:\b(?<Word>\w+)\b\s+\k<Word>\b,等效于\b(\w+)\b\s+\1\b。

1.7.5零宽断言(?=exp)(?<=exp)(?!exp)(?<!exp)

接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。最好还是拿例子来说明吧:

断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。

(?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc。

(?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。

假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})+\b,用它对1234567890进行查找时结果是234567890。

下面这个例子同时使用了这两种断言:(?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)。

(?!exp)零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。

同理,我们可以用(?<!exp)零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。

一个更能表现零宽断言的真正用途的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。(<?(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是<b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的"\/",使用了转义符,这个也可以不转义直接用"/";"\1"则是一个引用,引用了1号分组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括<b>和</b>本身)。

1.7.6贪婪和懒惰匹配

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面1.4中给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:

a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab和ab两个。

1.8处理选项

表三:为一些常用的处理选项,可在程序设计语言中实现

表三常用的处理选项
名称 说明
IgnoreCase(忽略大小写) 匹配时不区分大小写。
Multiline(多行模式) 更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.)
Singleline(单行模式) 更改.的含义,使它与每一个字符匹配(包括换行符\n)。
IgnorePatternWhitespace(忽略空白) 忽略表达式中的非转义空白并启用由#标记的注释。
ExplicitCapture(显式捕获) 仅捕获已被显式命名的组。

以C#语言为例,可以使用Regex(String, RegexOptions)构造函数来设置正则表达式的处理选项。如:Regex regex = new Regex(@"\ba\w{6}\b", RegexOptions.IgnoreCase);

1.9更多的学习

更多的有关正则表达式的学习可以去参考微软的开发人员指南http://msdn.microsoft.com/zh-cn/library/az24scfc(v=VS.90).aspx

1.10更多的例子

可以参考百度百科http://baike.baidu.com/view/94238.htm?fr=ala0_1

1.11本文内容参考摘要

天极网《Java正则表达式详解》,2005年;

deerchao《正则表达式30分钟入门教程》,2009年。


本文对我无帮助,减1分本文对我有帮助,加1分(本文对您有帮助吗?目前总+1分,1参与者。)
Loading ... Loading ...

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.