1.什么是正则表达式(regular expression):
简单地说,正则表达式(经常被简称为模式)是一些用来匹配和处理文本的字符串。另外正则表达式语言并不是一种完备的程序设计语言,它甚至算不上是一种能够直接安装并运行的程序。更准确地说,正则表达式语言是内置于其它语言或软件里的”迷你“语言。另外说一下正则表达式引擎,正则表达式引擎是一种可以处理正则表达式的软件。通常,引擎是更大的应用程序的一部分。在软件世界,不同的正则表达式并不互相兼容。
PS:本文将用<<regex>>来表示一段具体的正则表达式
2.常用语法:
2.1普通字符:最基本的正则表达式由单个文字符号组成。如<<a>>('<‘、’>’是普通字符),它将匹配字符串中第一次出现的字符“a”(绝大数正则 表达式引擎的默认行为是只返回第一个匹配结果)。如对字符串“Jack is a boy”。“J”后的“a”将被匹配。而第二个“a”将不会被匹配。正则表达式也可以匹配第二个“a”,这必须是你告诉正则表达式引擎从第一次匹配的地方开始搜索。在文本编辑器中,你可以使用“查找下一个”。在编程语言中,会有一个函数可以使你从前一次匹配的位置开始继续向后搜索。类似的,<<cat>>会匹配“About cats and dogs”中的“cat”。这等于是告诉正则表达式引擎,找到一个<<c>>,紧跟一个<<a>>,再跟一个<<t>>。
PS:正则表达式引擎缺省是大小写敏感的。除非你告诉引擎忽略大小写,否则<<cat>>不会匹配“Cat”。
2.2 特殊字符: 对于文字字符,有12个字符被保留作特殊用途, 这些特殊字符也被称作元字符。他们是:
[ ] ^ $ . | ? * + ( )
如果你想在正则表达式中将这些字符用作文本字符,你需要用反斜杠“”对其进行转义。
例如你想匹配“1+1=2”,正确的表达式为<<1/+1=2>>。
PS:在编程语言中,一些特殊的字符会先被编译器处理(//编译处理后为/),然后再传递给正则引擎。因此正则表达式<<1+2=2>>在编程语言中要写成<<1\+1=2>>。为了匹配“C: emp”,你要用正则表达式<<C:\temp>>。而在编程语言中,正则表达式则变成了“C:\\temp”。
2.3不可显示字符:可以使用特殊字符序列来代表某些不可显示字符。
如:<< >>代表Tab << >>代表回车符 << >>代表换行符
PS:Windows中文本文件使用“ ”来结束一行而Linux/Unix使用“ ”。
2.4使用“.”匹配任意单个字符(除换行符):
在正则表达式中,“.”是最常用的符号之一。不幸的是,它也是最容易被误用的符号之一。“.”匹配一个单个的字符而不用关心被匹配的字符是什么。唯一的例外是新行符。这个例外是因为历史的原因。因为早期使用正则表达式的工具是基于行的。它们都是一行一行的读入一个文件,将正则表达式分别应用到每一行上去。在这些工具中,字符串是不包含新行符的。因此“.”也就从不匹配新行符。
PS:点号可以说是最强大的元字符。它允许你偷懒:用一个点号,就能匹配几乎所有的字符。但是问题在于,它也常常会匹配不该匹配的字符。你想要你的正则表达式达到如何完美的程度取决于你想达到什么样的目的。如果你想校 验用户输入,则需要尽可能的完美。如果你只是想分析一个已知的源,并且我们知道没有错误的数据,用一个比较好的正则表达式来匹配你想要搜寻的字符就已经足够。
2.5使用[]匹配一组字符:
字符集是由一对方括号“[]”括起来的字符集合。使用字符集,你可以告诉正则表达式引擎仅仅匹配多个字符中的一个。如果你想匹配一个“a”或一个 “e”,使用<<[ae]>>。你可以使用<<gr[ae]y>>匹配gray或grey。这在你不确 定你要搜索的字符是采用美国英语还是英国英语时特别有用。相反,<<gr[ae]y>>将不会匹配graay或graey。字符 集中的字符顺序并没有什么关系,结果都是相同的。
你可以使用连字符“-”定义一个字符范围作为字符集。<< [0-9]>>匹配0到9之间的单个数字。你可以使用不止一个范围。<<[0-9a-fA-F] >>匹配单个的十六进制数字,并且大小写不敏感。你也可以结合范围定义与单个字符定义。<<[0-9a-fxA- FX]>>匹配一个十六进制数字或字母X。再次强调一下,字符和范围定义的先后顺序对结果没有影响。
这个语法很简单,我就简单举个例子即可<< [Aa][A-F][^0-9] >>,这个正则表达式长度为三个字符,第一个字符可以匹配A或a;第二个字符可以匹配A~F中的任意一个字符,这叫做字符集合区间;第三个字符可以匹配除0~9外的其它字符。
PS:^的效果将作用于给定字符集合里的所有字符或字符区间,而不是仅限于紧跟在字符后面的那一个字符或字符区间,另外不像“.”,取反字符集是可以匹配回车换行符的。。
2.6一些常用元字符:
d: 相当于[0-9] D:相当于[^0-9],即任何一个非数字字符 w:[a-zA-Z0-9_],相当于编程语言中合法字符的首字母
W:[^a-zA-Z0-9_] s任何一个空白字符,[f v]
2.7重复匹配 :
?:告诉引擎匹配前导字符0次或一次。事实上是表示前导字符是可选的。
+:告诉引擎匹配前导字符1次或多次
*:告诉引擎匹配前导字符0次或多次
{}:告诉引擎匹配前导字符匹配重复次数,如{5}重复前导字符4次,{2,4}重复前导字符2~4次,{2,}至少重复前导字符2次
注意贪婪性: 假设你想用一个正则表达式匹配一个HTML标签。你知道输入将会是一个有效的HTML文件,因此正则表达式不需要排除那些无效的标签。所以如果是在两个尖括号之间的内容,就应该是一个HTML标签。许多人会首先想到用正则表达式<< <.+> >>,他们会很惊讶的发现,例如对于测试字符串,“This is <strong>life</strong> world”,你可能期望会返回<strong>,然后继续进行匹配的时候,返回</strong>。但事实是不会。正则表达式将会 匹配“<strong>life</EM>”。很显然这不是我们想要的结果。原因在于“+”是贪婪的。也就是说,“+”会导致正则表达 式引擎试图尽可能的重复前导字符。只有当这种重复会引起整个正则表达式匹配失败的情况下,引擎会进行回溯。也就是说,它会放弃最后一次的“重复”,然后处 理正则表达式余下的部分。和“+”类似,“?*”的重复也是贪婪的。
用懒惰性取代贪婪性:一个用于修正以上问题的可能方案是用“+”的惰性代替贪婪性。你可以在“+”后面紧跟一个 问号“?”来达到这一点。“*”,“{}”和“?”表示的重复也可以用这个方案。因此在上面的例子中我们可以使用“<.+?>”。让我们再来 看看正则表达式引擎的处理过程。再一次,正则表达式记号“<”会匹配字符串的第一个“<”。下一个正则记号是“.”。这次是一 个懒惰的“+”来重复上一个字符。这告诉正则引擎,尽可能少的重复上一个字符。因此引擎匹配“.”和字符“s”,然后用“>”匹配“t”,结果失败 了。引擎会进行回溯,和上一个例子不同,因为是惰性重复,所以引擎是扩展惰性重复而不是减少,于是“<.+”现在被扩展为“<st”。引擎继 续匹配下一个记号“>….直到成功匹配。引擎于是报告“<strong>”是一个成功的匹配。整个过程大致如此。
2.8 单词边界
元字符<<>>也是一种对位置进行匹配的“锚”。这种匹配是0长度匹配。
有4种位置被认为是“单词边界”:
- 在字符串的第一个字符前的位置(如果字符串的第一个字符是一个“单词字符”)
- 在字符串的最后一个字符后的位置(如果字符串的最后一个字符是一个“单词字符”)
- 在一个“单词字符”和“非单词字符”之间,其中“非单词字符”紧跟在“单词字符”之后
- 在一个“非单词字符”和“单词字符”之间,其中“单词字符”紧跟在“非单词字符”后面
“单词字符”是可以用“w”匹配的字符,“非单词字符”是可以用“W”匹配的字符。在大多数的正则表达式实现中,“单词字符”通常包括<<[a-zA-Z0-9_]>>。
例如:<<4>>能够匹配单个的4而不是一个更大数的一部分。这个正则表达式不会匹配“44”中的4。
换种说法,几乎可以说<<>>匹配一个“字母数字序列”的开始和结束的位置。
- 深入正则表达式引擎内部(很重要)
让我们看看把正则表达式<<is>>应用到字符串“This island is beautiful”。引擎先处理符号<<>>。因为是0长度 ,所以第一个字符T前面的位置会被考察。因为T是一个“单词字符”,而它前面的字符是一个空字符(void),所以匹配了单词边界。接着<<i>>和第一个字符“T”匹配失败。匹配过程继续进行,直到第五个空格符,和第四个字符“s”之间又匹配了<<>>。然而空格符和<<i>>不匹配。继续向后,到了第六个字符“i”,和第五个空格字符之间匹配了<<>>,然后<<is>>和第六、第七个字符都匹配了。然而第八个字符和第二个“单词边界”不匹配,所以匹配又失败了。到了第13个字符i,因为和前面一个空格符形成“单词边界”,同时<<is>>和“is”匹配。引擎接着尝试匹配第二个<<>>。因为第15个空格符和“s”形成单词边界,所以匹配成功。引擎“急着”返回成功匹配的结果。
2.9 字符串开始和结束的锚定
锚定和一般的正则表达式符号不同,它不匹配任何字符。相反,他们匹配的是字符之前或之后的位置(同单单词锚定原理一样)。“^”匹配一行字符串第一个字符前的位置。<<^a>>将会 匹配字符串“abc”中的a。<<^b>>将不会匹配“abc”中的任何字符。类似的,$匹配字符串中最后一个字符的后面的位置。所以<<c$>>匹配“abc”中的c。
- 锚定的简单应用
在编程语言中校验用户输入时,使用锚定是非常重要的。如果你想校验用户的输入为整数,用<<^d+$>>。
用户输入中,常常会有多余的前导空格或结束空格。你可以用<<^s*>>和<<s*$>>来匹配前导空格或结束空格。
2.10 选择符
正则表达式中“|”表示选择。你可以用选择符匹配多个可能的正则表达式中的一个。
如果你想搜索文字“cat”或“dog”,你可以用<<cat|dog>>。如果你想有更多的选择,你只要扩展列表<<cat|dog|mouse|fish>>。
选择符在正则表达式中具有最低的优先级,也就是说,它告诉引擎要么匹配选择符左边的所有表达式,要么匹配右边的所有表达式。你也可以用圆括号来限制选择符的作用范围。如<<(cat|dog)>>,这样告诉正则引擎把(cat|dog)当成一个正则表达式单位来处理。
- 注意正则引擎的“急于表功”性
正则引擎是急切的,当它找到一个有效的匹配时,它会停止搜索。因此在一定条件下,选择符两边的表达式的顺序对结果会有影响。假设你想用正则表达式搜索一个编程语言的函数列表:Get,GetValue,Set或SetValue。一个明显的解决方案是<<Get|GetValue|Set|SetValue>>。让我们看看当搜索SetValue时的结果。
因为<<Get>>和<<GetValue>>都失败了,而<<Set>>匹配成功。因为正则导向的引擎都是“急切”的,所以它会返回第一个成功的匹配,就是“Set”,而不去继续搜索是否有其他更好的匹配。
和我们期望的相反,正则表达式并没有匹配整个字符串。有几种可能的解决办法。一是考虑到正则引擎的“急切”性,改变选项的顺序,例如我们使用<<GetValue|Get|SetValue|Set>>,这样我们就可以优先搜索最长的匹配。我们也可以把四个选项结合起来成两个选项:<<Get(Value)?|Set(Value)?>>。因为问号重复符是贪婪的,所以SetValue总会在Set之前被匹配。
一个更好的方案是使用单词边界:<<(Get|GetValue|Set|SetValue)>>或<<(Get(Value)?|Set(Value)?>>。更进一步,既然所有的选择都有相同的结尾,我们可以把正则表达式优化为<<(Get|Set)(Value)?>>。
2.11 组与向后引用(特别重要)
把正则表达式的一部分放在圆括号内,你可以将它们形成组。然后你可以对整个组使用一些正则操作,例如重复操作符。
要注意的是,只有圆括号“()”才能用于形成组。“[]”用于定义字符集。“{}”用于定义重复操作。
当用“()”定义了一个正则表达式组后,正则引擎则会把被匹配的组按照顺序编号,存入缓存。当对被匹配的组进行向后引用的时候,可以用“数字”的方式进行引用。<<1>>引用第一个匹配的后向引用组,<<2>>引用第二个组,以此类推,<< >>引用第n个组。而<<