正规表达式教程
正则表达式 正则表达式这个名词,相信很多人都听说过,这个名词最早起源于1956 年,一位叫 Stephen Kleene 的美国数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为“神经网事件的表示法”的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为“正则集的代数”的表达式,因此采用“正则表达式”这个术语。 随后,发现可以将这一工作应用于使用Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson是Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的qed 编辑器。 Q: 正则表达式,能够为我们做什么呢? A: 基于文本的编辑器和搜索工具中的一个重要部分。正则表达式可以让用户通过使用一系列的特殊字符构建匹配模式,然后把匹配模式与数据文件、程序输入以及WEB页面的表单输入等目标对象进行比较,根据比较对象中是否包含匹配模式,执行相应的程序。 下面我们就一步一步的结合它的语法,来介绍正则表达式的使用。 二、初次接触正则表达式 我们先来了解正则表达式的一些基本概念。正则表达式作为一种表示语言,其定义了自己的一套描述方式,来描述各种各样的字符类。下面摘取msdn中的一段定义。(ms-help: //MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconcharacterclasses.htm) 字符转义表 . [aeiou] [^aeiou] [0-9a-fA-F] p{name} P{name} w W s S d D 上表列举了,正则表达式中最最基本的语法定义,了解这些,我们已经可以定义一些简单的规则了,例如: 1. 匹配所有的字符 当然是什么都不用写(@_@) 2. 匹配所有的英文字符 a) w b) [a-zA-Z_0-9] 3. 匹配十进制数字 a) d b) [0-9] 看上面的例子,是不是觉得很简单呢,不过,到目前为止,这样写出来的规则,还有一个很大的缺陷,就是没有声明匹配字符的个数? Q: 我希望要匹配的字符为5个英文字母 A: ??? 光了解上面的知识是,无法解决这个的L。那正则表达式中是如何解决这个问题的呢,我们来看下面这个表: (ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconquantifiers.htm) 限定符表 * + ? {n} {n,} {n,m} *? +? ?? {n}? {n,}? {n,m}? 上表中列出了,正则表达式的限定方式,配合这些字符的使用,我们就可以很方便的编写更为强劲的正则表达式了。 例如: 1. 匹配零个或多个所有的字符 * 2. 匹配一个或多个所有字符 + 3. 匹配零个或多个所有的英文字符 w* 4. 匹配一个或多个所有的英文字符 [a-zA-Z0-9]+ 5. 匹配3个十进制数字 d{3} 6. 匹配最少3个十进制数字 d{3,} 7. 匹配3个到6个十进制数字 d{3,6} 现在我们可以解答上面问题了: Q: 我希望要匹配的字符为5个英文字母 A: w{5} 很高兴,我们已解决了上面的问题,不过,新的问题总是在不断的出现。我如何限制匹配字符出现在哪里呢? Q: 我希望匹配以doc开头的字符串 A: ??? 为了解决这个问题,我们先来看看这个表: (ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconatomiczero-widthassertions.htm) 原子零宽度断言 ^ $ A Z z G b B 相信大家都注意到了,在这个表中第一个断言字符就是我们需要的@_@. 例如,^ 指定当前位置在行或字符串的开头。因此,正则表达式 ^FTP 只会返回那些在行的开头出现的字符串“FTP”的匹配项。 看来上面碰到的问题,又可以解决了,让我们一起来解决上面的问题: Q: 我希望匹配以doc开头的字符串 A: ^doc 以上我们初步了解了什么是正则表达式,已经了解其最基本的语法,当作热身@_@,接下来,才正式进入主题,我们会从第二篇开始深入探讨正则表达式的使用。 首先,我们先看几个实际的例子: 1. 验证输入字符是否全部为英文字符 java script: var ex = "^w+$"; var re = new RegExp(ex,"i"); return re.test(str); vb script Dim regEx,flag,ex ex = "^w+$" Set regEx = New RegExp regEx.IgnoreCase = True regEx.Global = True regEx.Pattern = ex flag = regEx.Test( str ) C# System.String ex = @"^w+$"; System.Text.RegularExpressions.Regex reg = new Regex( ex ); bool flag = reg.IsMatch( str ); 2. 验证邮件格式 C# System.String ex = @"^w+@w+.w+$"; System.Text.RegularExpressions.Regex reg = new Regex( ex ); bool flag = reg.IsMatch( str ); 3. 更改日期的格式(用 dd-mm-yy 的日期形式代替 mm/dd/yy 的日期形式) C# String MDYToDMY(String input) { return Regex.Replace(input, "b(?<month>d{1,2})/(?<day>d{1,2})/(?<year>d{2,4})b", "${day}-${month}-${year}"); } 4. 从 URL 提取协议和端口号 C# String Extension(String url) { Regex r = new Regex(@"^(?<proto>w+)://[^/]+?(?<port>:d+)?/", RegexOptions.Compiled); return r.Match(url).Result("${proto}${port}"); } 这里的例子可能是我们在网页开发中,通常会碰到的一些正则表达式,尤其在第一个例子中,给出了使用java script,vb script,C#等不同语言的实现方式,大家不难看出,对于不同的语言来说,正则表达式没有区别,只是正则表达式的实现类不同而已。而如何发挥正则表达式的公用,也要看实现类的支持。 (摘自msdn: Microsoft .NET 框架 SDK 提供大量的正则表达式工具,使您能够高效地创建、比较和修改字符串,以及迅速地分析大量文本和数据以搜索、移除和替换文本模式。ms-help: //MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconregularexpressionslanguageelements.htm) 下面我们逐个来分析这些例子: 1-2,这两个例子很简单,只是简单的验证字符串是否符合正则表达式规定的格式,其中使用的语法,在第一篇文章中都已经介绍过了,这里做一下简单的描述。 第1个例子的表达式: ^w+$ ^ -- 表示限定匹配开始于字符串的开始 w – 表示匹配英文字符 + -- 表示匹配字符出现1次或多次 $ -- 表示匹配字符到字符串结尾处结束 验证形如asgasdfs的字符串 第2个例子的表达式: ^w+@w+.w+$ ^ -- 表示限定匹配开始于字符串的开始 w – 表示匹配英文字符 + -- 表示匹配字符出现1次或多次 @ -- 匹配普通字符@ . – 匹配普通字符.(注意.为特殊字符,因此要加上转译) $ -- 表示匹配字符到字符串结尾处结束 验证形如dragontt@sina.com的邮件格式 第3 个例子中,使用了替换,因此,我们还是先来看看正则表达式中替换的定义: (ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconsubstitutions.htm) 替换 $123 ${name} $$ $& $` $' $+ $_ 分组构造 分组构造 ( ) (?<name> ) (?<name1-name2> ) (?: ) (?imnsx-imnsx: ) (?= ) (?! ) (?<= ) (?<! ) (?> ) 我们还是先简单的了解一下这两个概念: 分组构造: 最基本的构造方式就是(),在左右括号中括起来的部分,就是一个分组; 更进一步的分组就是形如:(?<name> )的分组方式,这种方式与第一种方式的不同点,就是对分组的部分进行了命名,这样就可以通过该组的命名来获取信息; (还有形如(?= )等等的分组构造,我们这篇的例子中也没有使用到,下次我们在来介绍) 替换: 上面提到了两种基本的构造分组方式()以及(?<name> ),通过这两种分组方式,我们可以得到形如$1,${name}的匹配结果。 这样说,可能概念上还是有些模糊,我们还是结合上面的例子来说: 第三个例子的正则表达式为:b(?<month>d{1,4})b (解释一下,为什么这里都是一起用:这里是C#的例子,在C#语言中是转译字符,要想字符串中的不转译,就需要使用或者在整个字符串的开始加上@标记,即上面等价与 @”b(?<month>d{1,2})/(?<day>d{1,2})/(?<year>d{2,4}b”) b -- 是一种特殊情况。在正则表达式中,除了在 [] 字符类中表示退格符以外,b 表示字边界(在 w 和 W 字符之间)。在替换模式中,b 始终表示退格符 (?<month>d{1,2}) – 构造一个名为month的分组,这个分组匹配一个长度为1-2的数字 / -- 匹配普通的/字符 (?<day>d{1,2}) --构造一个名为day的分组,这个分组匹配一个长度为1-2的数字 / -- 匹配普通的/字符 (?<year>d{2,4}b”) --构造一个名为year的分组,这个分组匹配一个长度为2-4的数字 这里还不能够看出这些分组的作用,我们接着看这一句 ${day}-${month}-${year} ${day} – 获得上面构造的名为day的分组匹配后的信息 - -- 普通的-字符 ${month} --获得上面构造的名为month的分组匹配后的信息 - -- 普通的-字符 ${year} --获得上面构造的名为year的分组匹配后的信息 举例来说: 将形如04/02/2003的日期使用例3种的方法替换 (?<month>d{1,2}) 分组将匹配到04由${month}得到这个匹配值 (?<day>d{1,2}) 分组将匹配到02由${day}得到这个匹配值 (?<year>d{1,2}) 分组将匹配到2003由${year}得到这个匹配值 了解了这个例子后,我们在来看第4个例子就很简单了。 第4个例子的正则 ^(?<proto>w+)://[^/]+?(?<port>:d+)?/ ^ -- 表示限定匹配开始于字符串的开始 (?<proto>w+) – 构造一个名为proto的分组,匹配一个或多个字母 : -- 普通的:字符 // -- 匹配两个/字符 [^/] – 表示这里不允许是/字符 +? – 表示指定尽可能少地使用重复但至少使用一次匹配 (?<port>:d+) – 构造一个名为port的分组,匹配形如:2134(冒号+一个或多个数字) ? – 表示匹配字符出现0次或1次 / -- 匹配/字符 最后通过${proto}${port}来获取两个分组构造的匹配内容 (有关Regex对象的用法,参考 ms-help://MS.VSCC/MS.MSDNVS.2052/cpref/html/frlrfSystemTextRegularExpressionsRegexMembersTopic.htm) 好了,本次介绍的几个例子,也讲得差不多了,希望大家有所收获,下次,在就一些特殊的要求,进一步探讨正则表达式的实现。 这里,我先提几个问题,然后,我们逐个运用正则表达式的知识来解决。 1. 符合两种条件之一,都成立,例如:是纯数字或者纯字符 123(true),hello(true),234.test23(false) 2. 要得到不以数字开头的字符组合 如:How2234do>you234do,希望得到How和you而不是do,do 3. 得到以数字开头的字符组合 上例中,得到do和do 4. 要得到不以数字结尾的字符组合 还是上面的情况,要得到的是Ho,do,yo,do 5. 得到以数字结尾的字符组合 同上例,得到Ho,do 6. 不允许字符中ab同时出现 例:nihaoma(true),above(false),agoodboy(true) 下面我们开始着手解决这些问题: 第一个:符合两种条件之一,都成立 这种要求可能代表着一种普遍的要求,我们先来看看这个表 替换构造 | (?(expression)yes|no) (?(name)yes|no) (ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconalternationconstructs.htm) 在这个表中,我们看到,正则中为了解决这一类问题,定义了|来表示或者的关系,就好像常见的或运算符一样,现在我们来看看如何利用|来解决我们的问题。 1. 先为可选择的表达式撰写表达式: a) 纯数字 – [0-9]* b) 纯字母 – [a-zA-Z]* 2. 将可选条件用|连接起来就是我们所需的 ^[0-9]*$|^[a-zA-Z]*$ (这里我特别对两个条件加上了^和$限定符,这在验证字符串是否完全符合要求时,是十分必要的,如果不加这两个限定符,有兴趣的朋友可以自己试一下效果。 后面四个问题,其实是一类的,所以我们把它们放在一起处理。接下来我们来解决第二到第四个问题: 首先,我们回顾一下上次介绍的分组构造: (?= ) (?! ) (?<= ) (?<! ) 可以看到,这个表的这四种规则,正好可以解决我们的问题。 @_@先解决我们的问题再说: 第二例:要得到不以数字开头的字符组合 (?<!d)[a-zA-Z]{2,} (?<!d) -- 限定字符的开头不为数字才匹配 [a-zA-Z]{2,} – 描述匹配2个以上的字母 (注:这是取巧的做法,因为,按照我们的逻辑How2234do>you234do中的两个do的o字母也是符合的,不过,这不是我们想要的,当然还有其他的解决办法,可以根据实际的情况来处理,这里是为了讲解这个方法@_@) 第三例:得到以数字开头的字符组合 (?<=d)[a-zA-Z]+ (?<=d) – 限定为数字开头的字符才匹配 [a-zA-Z]+ -- 描述匹配1个或多个字母 第四例:要得到不以数字结尾的字符组合 [a-zA-Z]+(?!d) [a-zA-Z]+ -- 描述匹配1个或多个字母 (?!d) – 限定不为数字结尾的字母才匹配 第五例:得到以数字结尾的字符组合 [a-zA-Z]+(?=d) [a-zA-Z]+ -- 描述匹配1个或多个字母 (?=d) – 限定为数字结尾的字母才匹配 第六例:不允许字符中ab同时出现 ^(?!.*?ab).*$ (?!.*?ab) – 限定不允许出现ab相连的字符 .* -- 任意字符 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |