官方源码中看到了对《Mastering Regular Expressions, 2nd Edition》的推荐;由Jeffrey E.F. Friedl大师主刀,O'Reilly于2002年再版。对O'Reilly的书向有好感,像当年误入java的歧途,没看Java编程思想之类的,倒看了O'Reilly的一本影印版《java in a nutshell》,颇留记忆。 正则表达式的“祖先”可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。1956 年, 一位叫 Stephen Kleene 的数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为“神经网事件的表示法”的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为“正则集的代数”的表达式,因此采用“正则表达式”这个术语。
随后,发现可以将这一工作应用于使用 Ken Thompson 的计算搜索算法的一些早期研究,Ken
Thompson 是 Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的 qed 编辑器。
目前,正则表达式已经在很多软件中得到广泛的应用,包括*nix(Linux, Unix等),HP等操作系
统;PHP,Perl,Python,C#,Java等开发环境,以及很多的应用软件中,For Example:网络上的搜索引擎,数据库的全文检索etc...
本笔记是是自我学习过程的一个整理,例子或来源于书本,或自己枚举。好了,废话一箩筐,切入正题。
1. 正则表达式的介绍
1.1、 行开始和结束
^begin line。匹配行开头,如^cat匹配以cat开头的
$end line。匹配行结束,如cat$匹配以cat结束的;^cat$仅仅匹配该行有cat
1.2、 匹配给定的字符序列
[...],表示in。里面写入欲匹配的几个字符,如 语言分别做了相同的预定义。 1.3、 匹配非给定的字符(非...) [^]匹配,表示not。^和行开头的标记完全一样,但写的位置不一样,则表述的意思可能完全相反,用^表示否定的意思,更多是写在[]里面,如:q[^u]匹配q后面紧跟非u的字符,如Iraqi,qasida,zaqqum,Iraq;没错,\"Iraq\"这个单词也会被匹配,尽管q后面什么也没有,也可能有个空格、或回车符等。否定字符的意思(翻译出来绕口):means \"match a character that's not listed\" and not \"don't match what is listed.\" 1.4、 匹配任何字符 .匹配,表示any。任何字符,如07.04匹配:07_04,07-04,07 04,07.04 etc;如想要精确匹配07/04,07-04,or 07.04;需要写07[-./]04;没错当.在[]里面包含的时候,仅仅表示“.”字符而已,如果不在[]里面,需要转义\\\\. 如匹配形如x.y的小数:是[0-9]\\\\.[0-9],而非[0-9].[0-9] 1.5、 匹配几个给定的字符序列中的一个 |匹配,表示or。譬如gr[ea]y,也可以这样写:grey|gray 或 gr(a|e)y;表的意思完全一样,即只能匹配gray或grey这两个单词。再举个例子:假如我们读取一封邮件如: From: elvis@tabloid.org (The King) Subject: be seein' ya around Date: Thu, 31 Oct 96 11:04:13 Hi, ......... .................. ......... ......... ......... ......... ......... ......... yours smith 我们打算只读取邮件的From: Subject: Date:(发件人、主题、发送日期)三行内容,那么我们可以用这个表达式来完成:\"^(From|Subject|Date):\"。^表示行开头,匹配From或Subject或Date,后面紧跟:的内容;是的,当我们从指定的几个字符序列当中匹配其中的一个时候,需要借助()来对其进行分组,当然()的用途不仅体现在这里,后面还会赘述。|的匹配是就近匹配原则,所以这样的写法就错误了:^From|Subject|Date: ,这个表达式描述的意思是,只要^From 或者 Subject 或者 Date:。 1.6、 匹配单词边界与java中的转义字符 \\<、\\>分别匹配单词的开头或结尾,如查找cat这个单词:\\ 注:单词边界的匹配符并非所有版本的表达式语言都支持,至少在java中就不是这样。在java中定义了“\\b”匹配单词边界,“\\B”匹配非单词边界,而没有定义匹配单词开头或结尾的边界符。 根据Java语法的约定,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义;目前被预定义的有:\\b \ \\n \\f \\r \\\" \\' \\\\。分别表示退格符、制表符、换行符、分页符、回车符、双引号、单引号、反斜线。所以在java字符串中,除了这几个字符,其他任何必须用单斜线的写法都是错误。 因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释;它们是为将来扩展正则表达式语言保留的,呵呵。。。 例如,当解释为正则表达式时,字符串字面值 \"\\b\" 与单个退格字符匹配,而 \"\\\\b\" 与单词边界匹配。字符串 \"\\(hello\\)\" 是非法的,将导致编译时错误;要与字符串 (hello) 匹配,必须使用字符串字面值 \"\\\\(hello\\\\)\"。 小结: 字符 . 名称 含义 任何字符 给定的字符序列 非给定的字符序列 行开头 行结尾 单词开始边界 *不是所有表达语言都支持的 dot [?] [^?] ^ $ \\< \\> | character negated character caret dollar backslash less-than backslash greater-than 单词结束边界 *不是所有表达语言都支持的 or; bar 或,必须匹配给定中的一个字符序列 用于|的分界分组,或者作为捕获组 (?) parentheses 1.7、 数量词(匹配次数) X? X* X+ X{n} X,一次或一次也没有 X,零次或多次 X,一次或多次 X,恰好 n 次 X{n,} X,至少 n 次 X{n,m} X,至少 n 次,但是不超过 m 次 举例,我们想匹配 注:X{1,}等于X+ 1.8、 子表达式 想对于一个完整的表达式而言,中间用()起来的部分被称为子表达式,如:^(Subject|Date):,Subject|Date就是一个子表达式,而[]起来的部分不能成为子表达式,如H[1-6]+,1-6不能成为子表达式。 几个例子: 如匹配金额,如:$5、$44、$5.49等,可以这样写:\\$[0-9]+(\\.[0-9][0-9])?,分解成这样三个部分:\\$ 和 ...+ 和...?,标识匹配$符(加斜线表示匹配美元符,而不是行结束标记符),至少一个数字,可包含含有小数点的两位小数。 如匹配时间,如\"9:17 am\" 、 \"12:30 pm\",可以这样写:[0-9]?[0-9]:[0-9][0-9] (am|pm)。它会匹配9:17 am 或者 12:30 pm,但也匹配 99:99 pm。显然是不正确的,小时可有1位或2位,2位时候,第1位只能是1,第2位只能是0或1或2,那么小时部分可以写:(1[012]|[1-9])。分钟部分,第1位是0-5,第2位是0-9;于是正确的写法是:(1[012]|[1-9]):[0-5][0-9] (am|pm)。当我们采用2位的24小时制的时候,小时部分可以写为:0?[0-9]|1[0-9]|2[0-3]。 1.9、 反向引用 表达式在匹配时,表达式引擎会将小括号 \"( )\" 包含的表达式所匹配到的字符串记录下来。\"小括号包含的表达式所匹配到的字符串\" 不仅是在匹配结束后才可以使用,在匹配过程中也可以使用。表达式后边的部分,可以引用前面 \"括号内的子匹配已经匹配到的字符串\"。 引用方法是 \"\\\" 加上一个数字。重复搜索前面某个分组匹配的文本,例如,\\1代表分组1匹配的文本。如:(\\b[a-z]+\\b) +\\1可以用来匹配重复的单词,像go go, kitty kitty。首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母(\\b[a-z]+\\b),然后是1个或几个空白符 ,最后是前面匹配的那个单词(\\1)。 \"\\1\" 引用第1对括号内匹配到的字符串,\"\\2\" 引用第2对括号内匹配到的字符串……以此类推,如 果一对括号内包含另一对括号,则外层的括号先排序号。换句话说,哪一对的左括号 \"(\" 在前,那这一对就先排序号。 额滴神啊,第一章终于学习完了,笔记主要对举的例子做了详细翻译,文字描述部分没有多赘述。第一章还只是个入门介绍;第二章难度会提高很多了。To be continued.... 2. 扩展正则表达式的应用 在这一章,主要通过几个例子的应用,来介绍如何应用正则表达式来匹配文本和查找文本。本章所举的例子,都是用Perl语言完成,因此在本章开始,作者通过一个例子引入了对Perl的介绍。由于笔者能读懂perl写的例子,因此,在此略过这一部分的详细翻译,虽作昨日黄花,亦未逝其本色,仅作简单介绍: Perl是1980年后期Larry Wall先生发明的一种功能非常强大的脚本语言,其思想来源于多种编程语言。文本操作和正则匹配的许多想法,主要的来源于两种语言:awk、sed,这两种语言相对于传统的C或pascal,有相当大的不同。Perl语言可以跨多种平台运行(java的雏形?),包括Dos、Windows、MacOS、OS/2、VMS、Unix。Perl作为CGI编程的一种通用语言,有非常强大而灵活的文本操作和处理能力。 2.1、 一个正则表达式的例子 一行文本匹配实数的部分。前缀部分,[-+]?可以匹配是否包含有操作符,以及正数、或负数;小数部分可用(\\.[0-9]*)?,\\.表示小数点,而非通配符,[0-9]*表示小数点后跟零个或多个数字,(\\.[0-9]*)?表示整个小数可出现零次或一次,如5.12、5.1、5.、5对于(\\.[0-9]*)?都是满足匹配的。这样,就不难写出完整的正则表达式:^[-+]?[0-9]+(\\.[0-9]*)?$,它匹配32, -3.723, 和 +98.6这样的数字,但他并不完善,譬如就不能匹配.357这样没有整数部分部分的实数,当然用户可以在前面手工添加个0,即0.357,它便满足了匹配条件,所以我不想把它看作一个主要的问题。关于浮点数的例子,将会在第四章有几个有趣的写法,来适应这些问题。 2.2、 一个较复杂的正则表达式例子 让我们再举一个例子,允许某些时候输入华氏或摄氏温度的值,即允许输入单位为C或F的温度记录。我们可以用数字,后跟[CF]来匹配。假设我们不考虑小数部分,则表达式可写为: ^[-+]?[0-9]+[CF]$ 或 ([-+]?[0-9]+)([CF]),那么这两种写法有什么不同吗? 显然第二种写法,结构更明晰一些,因为对其做了分组处理(捕获组),且更容易扩展(反向引用)。据此来扩展这个应用:当我们输入华氏温度时候,想得到相应的摄氏温度,或输入摄氏温度时候,想得到相应的华氏温度,如(红色字体表示输入部分,蓝色字体表示输出部分): % perl -w convert Enter a temperature (i.e. 32F, 100C): 39F 3.89 C = 39.00 F % perl -w convert Enter a temperature (i.e. 32F, 100C): 39C 39.00 C = 102.20 F % perl -w convert Enter a temperature (i.e. 32F, 100C): oops Expecting a number, so don't understand \"oops. 用一个简单的流程图表示: 得到输入à验证输入是否合法à不合法à显示错误消息。 得到输入à验证输入是否合法à合法à得到输入的温度类型à如果是摄氏温度则计算对应的华氏温度,否则计算摄氏温度à输出计算结果。用Perl语言写出的代码段为: if ($input =~ m/^([-+]?[0-9]+)([CF])$/) { # If we get in here, we had a match. $1 is the number, $2 is \"C\" or \"F\". $InputNum = $1; # save to named variables to make the . . . $type = $2; # . . . rest of the program easier to read. if ($type eq \"C\") { # 'eq' tests if two strings are equal # The input was Celsius, so calculate Fahrenheit $celsius = $InputNum; $fahrenheit = ($celsius * 9 / 5) + 32; } else { # Gee, must be an \"F\" then, so calculate Celsius } $fahrenheit = $InputNum; $celsius = ($fahrenheit - 32) * 5 / 9; # at this point we have both temperatures, so display the results: printf \"%.2f C = %.2f F\\n\ } else { } 假设我们的程序结构如下: if ( logical test ) { . . . LOTS OF PROCESSING if the test result was true . . . } else { . . . just a bit of processing if the test result was false . . . } 任何学习过结构化编程语言的人都知道(应该知道),当出现if语言的逻辑分支处理的时候,处理的语句块有短的和长的两部分,最好把短的代码部分,放在if中处理,长的部分放在else中去处理任意if不能满足的情况,这样会是代码相对容易阅读和理解。 想完成这样的情况,我们需要反转test条件部分,“当..不匹配时”如何处理。这样在if条件中便是一个否定的匹配了。 继续上面的例子,我们已经实现了可以输入一个 整数+C或F的温度数。那么新的需求是允许输入浮点数的温度、且温度的单位可以是小写的c或f,且单位和数字之间有空格分隔;譬如:“98.6 f”将会被识别为一个合法的温度输入。 根据2.1部分的描述,我们知道一个实数可以表示为:([-+]?[0-9]+(\\.[0-9]*)?),那么满足的表达式是:([-+]?[0-9]+(\\.[0-9]*)?) + 若干空格 + 温度单位。 对于空格,在表达式中有多种,譬如可以是一个tab符,用\表示;可以是一个换行符,用\\n表示;可以是一个回车符,用\\r表示;可以是一个分页符,用\\f表示;也可以是一个垂直制表符,用\\x0B;也可以是一个空格字符,用“ ”表示。\\x表示一个16进制的的字符,16进制0B对应的ascii字符是“vt”。对于这些所有可见的或不可见的空白字符,在正则表达式中可用\\s来简化,即:\\s = [ \\\n\\x0B\\f\\r],顺 # The initial regex did not match, so issue a warning. print \"Expecting a number, so don't understand \\\"$input\\\".\\n\"; 便而言,\\S表示非空白字符,即:\\S = [^\\s] 那么现在,这个完整的表达式是:^([-+]?[0-9]+(\\.[0-9]*)?)\\s*([cfCF])$,用Perl可以写为:~ m/^([-+]?[0-9]+(\\.[0-9]*)?)\\s*([CF])$/i。/i是一个开关,意思是大小写不敏感的,在其他语言中也定义有这样的常量,如Java中Pattern. CASE_INSENSITIVE。正则表达式中更多有用的短字符: 字符 \ \\n \\r \\s \\s 含义 tab符 换行符(*nix系统中,大多用此表示文本的换行) 回车符(windows系统中,大多用\\r或\\r\\n表示文本的换行) 空白符(可见或不可见的i.e., space, tab, newline, formfeed) 任何非空白字符[^\\s] 单词字符[a-zA-Z0-9_],通常用\\w+匹配一个单词 任何非单词字符i.e., [^a-zA-Z0-9_]或[^\\w] 匹配数字[0-9] ,i.e., a digit 匹配非数字,i.e., [^0-9] \\w \\w \\d \\D 2.3、 利用正则表达式修改文本 截至目前为止,我们看到的都是利用正则表达式匹配的例子;现在开始将来学习用正则表达式来进行文本修改,例如Search/Replace,我们将利用Perl或其他语言工具来展示表达式语言这方面的特性。 我们知道,利用$var =~ m/regex/可以完成一个匹配,它将返回true或false。类似地,一个替换的表达式可写为:$var =~ s/regex/replacement/,当满足regex的匹配时,用replacement来对匹配的文本进行替换,它也适用于对捕获组的替换。看这几个例子:$var =~ s/Jeff/Jeffrey/;$var =~ s/\\bJeff\\b/Jeffrey/;$var =~ s/\\bJeff\\b/Jeff/i;分别表示替换Jeff为Jeffrey,替换单词Jeff为Jeffrey,替换单词Jeff且不分大小写为Jeffrey。 …… 3. 正则表达式特性概要 本章主要讲述了正则表达式的基础知识,分别在第1章和第2章涉及到了,在此不再做过多无谓的赘述,直接略过。 4. 正则表达式的机制 现在,我们已对于所要学习的东西有了初步的背景知识,接下来我们要对正则表达式如何工作的机制来进行深入的研究。这里我们不会过多的重复前3章的东西,这一章纯粹是深入正则表达式的机制,我们将时间花费在该付出的地方,所以期待大家能通过这一章节的学习来收获一些实际的工作经验(感受)。 4.1、 关于引擎 首先,关于引擎的知识我们了解多少?一个完整的引擎,它可以不在任何外力的作用下,自由的从A点移动到B点,可以代替你做许多重复冗余的工作;可以说,引擎的主要任务就是转动它的轮子,而排除掉你的任何多余的担忧,不是吗? 好,要是你有一辆电动汽车会怎么样?它只能大约能工作一段时间,而不是像普遍烧汽油的车那样,因为它不是那样设计的;你必须记着,它不是依靠汽油的燃烧来推动车前进,而是电能的消耗。如果你有一辆燃烧汽油的车,难意味着该车的引擎必须是汽油发动机,而不是像电动汽车那样装置的引擎,该引擎必须有火花塞、空气过滤器等;不管哪类引擎,最令人不安的是引擎性能的下降,或者干脆罢工。 每一种引擎都可以做各种各样不同的工作,但结果都是让轮子转动;这一点,你必须清楚,他们只是不同的设计和应用而已。 在加利福尼亚州,对汽车尾气的排放是有着严格标准的,即:加利福尼亚汽车尾气排放标准。一些汽车引擎会遵循这一标准,而非全部。没有遵循这一标准的各种各样的汽车引擎,他们不会在乎这一标准,你也不必了解这些汽车的引擎如果是如何实现的,因为它是电动汽车。所以汽车被分为了两类:电动汽车、汽油发动机汽车。 4.2、 正则表达式的引擎类型 正则表达式引擎有两种不同的类型:DFA(就像电动汽车)、NFA(就像汽油汽车),细节区别不大;两种引擎并行存在了好长时间,与汽油汽车类似,NFA这种引擎更被经常性使用。。。 4.3、 最先匹配原则(The Earliest Match Wins) 该原则的意思是,表达式引擎总是在给定的匹配字符串的左边试图匹配。如欲匹配:FLORAL这个单词的ORA字符,则首先从字符串的第一个字符开始进行匹配,则第一个匹配的是FLO,显然这是不正确的,然后再从第4个字符开始匹配,得到的是RAL,也是不正确的,继续从第2个字符开始匹配,得 到的是LOR,仍然是错误的,继续从第3个字符开始匹配,得到的字符是ORA,ok,匹配成功,可以算出引擎一共进行了4次匹配。 再举一个例子:The dragging belly indicates your cat is too fat,加入我们想从这句话中匹配cat这3个字符,则它匹配的是indicates,而不是随后的cat单词,因为indicates这个单词较早地在字符串中出现了;对于正则表达式fat|cat|belly|your,引擎会匹配到belly这个单词为止。 4.4、 贪婪匹配原则(Some Metacharacters Are Greedy) 对于最先匹配原则,不能做到对欲匹配字符串的完整扫描。这里就涉及到了数量词的概念,即贪婪匹配原则,总是尽可能多的匹配给定的表达式指定的次数。这些数量词(?, *, +, and {min, max}),都是贪婪匹配模式;就像 a in a? ,(expr) in (expr)* ,[0-9] in [0-9]+,他们都指定了匹配的最少次数。 首先定义个变量$line,值为Subject: Re: happy birthday,则下面的perl代码: if ( $line =~ m/^Subject: (.*)/) { print \"The subject is: $1\\n\"; } 则会输出如下结果:\"The subject is: Re: happy birthday\"。^Subject: (.*)/表示以Subject: 开头的,后面可有或可无的任何字符,主意“.”并不一定匹配换行符。对于如下的perl代码: if ( $line =~ m/^Subject: (Re: )?(.*)/ ) { print \"The subject is: $2\\n\"; } 则会输出结果:\"The subject is: happy birthday\" …… 5. 一些正则表达式的例子应用 通过前面的学习,已经对正则表达式有了相当的了解,本章就结合一些实际的例子,来体现正则表达式的应用。 5.1、 匹配时间的表达式 假如我们想匹配一个时间的正则表达式,如:2008-10-31 22:03:05,中间的-也可能是/或.即:2008.10.31 22:03:05或者2008/10/31 22:03:05那么这个正则表达式该如何写呢,思考一下。 首先年份1或2开头,4位数。月份是1-12,天使1-29、30、31。此处采用24H制,即0-23。分钟与秒相同,均是是0-60。那么这个正则表达式应该是: String strPattern=\"([12]\\\\d{3})([-./])(0?[1-9]|1[012])\\\\2(0?[1-9]|[12]\\\\d|30|31) ([01]?\\\\d|2[0123]):(0?\\\\d|[1-5]\\\\d):(0?\\\\d|[1-5]\\\\d)\"; 当然此处不严谨,没有精确地考虑天数,如1 3 5 7 8 10 12为31天,4 6 9 11为30天;2月平年28天,闰年29天。 我们一步一步来,首先把2月份只考虑为28天,其它月份的时间可能为30天或31天,那么该怎么写呢? 首先我们对strPattern进行下分解,日期strPattern1和时间strPattern2两部分: String strPattern1=\"([12]\\\\d{3})([-./])(0?[1-9]|1[012])\\\\2(0?[1-9]|[12]\\\\d|30|31)\"; String strPattern2=\"([01]?\\\\d|2[0123]):(0?\\\\d|[1-5]\\\\d):(0?\\\\d|[1-5]\\\\d)\"; OK,现在我们主要的精力就是关注strPattern1做处理了,加上对月份的判断后修改的日期为: strPattern1 =\"([12]\\\\d{3})([-./])\" + \"(\" + \"((0?[13578]|10|12])\\\\2(0?[1-9]|[12]\\\\d|30|31))\" + \"|((0?[2])\\\\2(0?[1-9]|[1]\\\\d|2[0-8]))\" + \"|((0?[469]|11])\\\\2(0?[1-9]|[12]\\\\d|30))\" + \")\"; 但是这样缺乏对2月份闰年的支持,2月平年28天,闰年29天,而关于闰年,公历闰年判定遵循的规律为:四年一闰,百年不闰,四百年再闰: 1、能被4整除而不能被100整除。(如2100年就不是闰年) 2、能被400整除。 这个是比较有难度的,由于正则表达式内部不能做计算,因此观察1000---3000内符合第一条的规 则,可得年份的规律的正则表达式如下: ([12]\\\\d)(0[48]|[2468][048]|[13579][26]) 能被400整除的数肯定能被100整除,因此后两位肯定是00,我们只要保证前两位能被4整除即 可,相应的正则表达式为: (1[26]|2[048])00 至于平年,则不符合闰年规则的,肯定就是平年了,所以一个完整的日期表达式为: strPattern1 =\"(((([12]\\\\d)(0[48]|[2468][048]|[13579][26]))\" + \"|((1[26]|2[048])00))([-./])\" + \"(((0?[13578]|10|12])\\\\8(0?[1-9]|[12]\\\\d|30|31))\" + \"|((0?[2])\\\\8(0?[1-9]|[12]\\\\d))\" + \"|((0?[469]|11])\\\\8(0?[1-9]|[12]\\\\d|30))\" + \"))|(([12]\\\\d{3})([-./])\" + \"(((0?[13578]|10|12])\\\\3(0?[1-9]|[12]\\\\d|30|31))\" + \"|((0?[2])\\\\3(0?[1-9]|[1]\\\\d|2[0-8]))\" + \"|((0?[469]|11])\\\\3(0?[1-9]|[12]\\\\d|30))\" + \"))\"; 再加上时间部分,可得到一个完整的表达式: String strPattern =\"((((([12]\\\\d)(0[48]|[2468][048]|[13579][26]))|((1[26]|2[048])00))([-./])(((0?[13578]|10|12])\\\\9(0?[1-9]|[12]\\\\d|30|31))|((0?[2])\\\\9(0?[1-9]|[12]\\\\d))|((0?[469]|11])\\\\9(0?[1-9]|[12]\\\\d|30))))|(([12]\\\\d{3})([-./])(((0?[13578]|10|12])\\\\4(0?[1-9]|[12]\\\\d|30|31))|((0?[2])\\\\4(0?[1-9]|[1]\\\\d|2[0-8]))|((0?[469]|11])\\\\4(0?[1-9]|[12]\\\\d|30))))) ([01]?\\\\d|2[0123]):(0?\\\\d|[1-5]\\\\d):(0?\\\\d|[1-5]\\\\d)\"; 5.2、 针对时间表达式的测试 String strPattern =\"((((([12]\\\\d)(0[48]|[2468][048]|[13579][26]))|((1[26]|2[048])00))([-./])(((0?[13578]|10|12])\\\\9(0?[1-9]|[12]\\\\d|30|31))|((0?[2])\\\\9(0?[1-9]|[12]\\\\d))|((0?[469]|11])\\\\9(0?[1-9]|[12]\\\\d|30))))|(([12]\\\\d{3})([-./])(((0?[13578]|10|12])\\\\4(0?[1-9]|[12]\\\\d|30|31))|((0?[2])\\\\4(0?[1-9]|[1]\\\\d|2[0-8]))|((0?[469]|11])\\\\4(0?[1-9]|[12]\\\\d|30))))) ([01]?\\\\d|2[0123]):(0?\\\\d|[1-5]\\\\d):(0?\\\\d|[1-5]\\\\d)\"; System.err.println(Pattern.matches(strPattern, \"2000/2/29 00:3:59\")); 22:03:5\")); 22:03:5\")); 22:33:5\")); 22:33:5\")); 22:33:5\")); //true //true //false //true //true //false System.err.println(Pattern.matches(strPattern, \"2004-2-29 System.err.println(Pattern.matches(strPattern, \"2007-2-29 System.err.println(Pattern.matches(strPattern, \"2008.2.29 System.err.println(Pattern.matches(strPattern, \"2008.10.31 System.err.println(Pattern.matches(strPattern, \"2008.9.31 至此,一个完美的判断时间的正则表达式就OK了;相信通过学习、分析、验证这样的一个正则表达式的例子,肯定会有不少收获,对正则表达式的理解和水平也会提高不少。 …. 因篇幅问题不能全部显示,请点此查看更多更全内容,
,
, etc.[a-z]代表从a到z中的任意字符,[0-9]、[A-Z]分别代表0-9,A-Z中的任意数字或大写字母;“-”代表连续的从开始字符到结束;那么[0123456789abcdefABCDEF]也可以写为[0-9a-fA-F];对于这些频繁使用的字符,各
或者
,则可以写为
;想匹配..
,则