10.正则表达式*一、简介在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。几乎所有的高级编程语言都支持正则表达式。正则表达式广泛应用于:检查文本中是否含有指定的特征词找出文中匹配特征词的位置从文本中提取信息,比如:字符串的子串修改文本正则表达式包括:只能匹配自身的普通字符(如英文字母数字标点等)和被转义了的特殊字符(称为“元字符”)。二、基本语法1.常用的元字符:符号描述.匹配除换行符“/n”以外的任意字符\\转义字符,匹配元字符时,使用“\\元字符”|表示可选项,即|前后的表达式任选一个^匹配字符串的开始$匹配字符串的结束()提取匹配的字符串,即括号内的看成一个整体,即指定子表达式[]可匹配方括号内任意一个字符{}前面的字符或表达式的重复次数:{n}表示重复n次;{n,}重复n次到更多次;{n,m}表示重复n次到m次*前面的字符或表达式重复0次或更多次+前面的字符或表达式重复1次或更多次?前面的字符或表达式重复0次或1次注:其它语言中的转义字符一般是“\”;若选中处理多行的选项,^和$就表示行的开始和结束;[aeiou]匹配任一元音字母;[.?!]匹配标点符号.或?或!2.特殊字符类与反义:符号描述\d与\D匹配1位数字字符,匹配非数字字符\s与\S匹配空白符,匹配非空白符\w与\W匹配字母或数字或下划线或汉字,匹配非\w字符\b与\B匹配单词的开始或结束的位置,匹配非\b的位置\h与\H匹配水平间隔,匹配非水平间隔\v与\V匹配垂直间隔,匹配非垂直间隔[^...]匹配除了…以外的任意字符注:空白符包括:空格、制表符、换行符、中文全角空格等;\S+匹配不包含空白符的字符串;\d同[0-9];\w只考虑英文的话同[-z0-9A-Z_];[^x]匹配除x之外的任意字符;[^aeiou]匹配除aeiou之外的任意字符,即匹配辅音字母;a[^]+匹配用尖括号括起来的以a开头的字符串。3.POSIX字符类,用[[:…:]]括起来的符号描述[[:lower:]]小写字母[[:upper:]]大写字母[[:alpha:]]所有大小写字母[[:digit:]]数字0-9[[:alnum:]]字母和数字[[:blank:]]空白符:空格、制表符、换行符、中文全角空格等[[:cntrl:]]控制字符[[:punct:]]标点符号:!”#%&’()*+-./:;等[[:space:]]空格字符:空格,制表符,垂直制表符,回车,换行符,换页符[[:xdigit:]]十六进制数字:0-9A-Fa-f[[:print:]]控制字符:[[:alpha:]],[[:punct:]],[[:space:]][[:graph:]]图形化字符:[[:alpha:]],[[:punct:]]4.正则表达式符号运算顺序圆括号括起来的表达式最优先,其次是表示重复次数的操作(即*+{});再次是连接运算(即几个字符放在一起,如abc);最后是表示可选项的运行(|)。三、实例例1查找hi,即精确匹配字符串:第1个字母是h,第2个字母是i一般有忽略大小写的选项,若忽略则匹配hi,HI,Hi,hI。但很多单词包含hi这两个连续字符,比如him,history,high等,也会被查到。若要精确匹配hi单词,可以用\bhi\b假如你要找的是hi后面不远处跟着一个Lucy,你应该用:\bhi\b.*\bLucy\b表示先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词。例2匹配电话号码:0开始,三位数字,连字号“-”,8位数字0\d{3}-\d{8}例3若干小例子:\ba\w*\b——匹配以a开头的单词说明:先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)\d+——匹配1个或更多连续的数字\b\w{6}\b——匹配刚好6个字符的单词^\d{5,12}$——匹配5位到12位的QQ号注意:若只用\d{5,12},则表示字符串里包含5-12位数字,而不是整个字符串是5-12位数字。deerchao\\.net——匹配deerchao.netC:\\\\Windows——匹配C:\Windows说明:前两个\\用来转义,后两个\\是因为在R中反斜杠\是用\\表示的,匹配的时候可能还需要:[\\]。Windows\d+——匹配Windows后面跟1个或更多数字^\w+——匹配整个字符串的第一个单词(或多行模式下,匹配一行的第一个单词)例4匹配电话号码,使用\(?0\d{2}[)-]?\d{8}可匹配如下几种格式的电话号码:(010)88886666或022-22334455或02912345678说明:首先是一个转义字符\(,接着?表示它能出现0次或1次;然后是一个0,后面跟着2个任意数字;其次是空格或)或-中的一个,跟着的?表示它出现1次或不出现;最后是8个任意数字。不幸的是,上面表达式也能匹配010)12345678或(022-87654321这样的“不正确”的格式。怎么避免?使用分枝条件:0\d{2}-\d{8}|0\d{3}-\d{7}可匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号如010-12345678,一种是4位区号,7位本地号0376-2233445\(?0\d{2}\)?[-]?\d{8}|0\d{2}[-]?\d{8}匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。注意:使用分枝条件时,要注意各个条件的顺序,因为匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管后面的条件了。例如,\d{5}-\d{4}|\d{5}匹配美国的邮政编码,美国邮编的规则是5位数字,或者用连字号间隔的9位数字;若改成\d{5}|\d{5}-\d{4}则只会匹配5位的邮编,或9位邮编的前5位。例5IP地址匹配,若使用(\d{1,3}\.){3}\d{1,3}说明:\d{1,3}\.匹配1到3位的数字再加一个句点.;然后{3}表示前面的子串重复三次;最后再加上一个1到3位数字。不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。用算术计算的话可能容易解决该问题,但正则表达式不支持任何数学功能。只能冗长地解决:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)例6(反向引用)语法:用于重复搜索前面某个分组匹配的文本,每个子表达式默认有1个分组号:1,2,…,\1表示分组1匹配的文本。例如,匹配重复的单词,像gogo,或者kittykitty\b(\w+)\b\s+\1\b说明:首先是\b(\w+)\b,表示一个单词,会被捕获到组号为1的分组中;然后\s+表示一个或多个空白符;最后是从组号1中捕获的内容,即刚才那个单词。也可以自己指定组名:\b(?Word\w+)\b\s+\kWord\b效果同上,指定组名为Word并引用它,其中换成''也可以。例7(零宽断言,语法略)匹配以ing结尾的单词的前面部分(除了ing以外的部分):\b\w+(?=ing\b)例如查找I'msingingwhileyou'redancing.时,它会匹配sing和danc匹配以re开头的单词的后半部分(除了re以外的部分):(?=\bre)\w+\b例如查找readingabook时,它匹配ading匹配以空白符间隔的数字(强调:不包括这些空白符):(?=\s)\d+(?=\s)若要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),可以这样查找需要在前面和里面添加逗号的部分:((?=\d)\d{3})+\b一个实际应用,匹配不包含属性的简单HTML标签内里的内容:(?=(\w+)).*(?=\/\1)说明:(?=(\w+))指定了这样的前缀:被尖括号括起来的单词比如可能是b;然后是.*表示任意的字符串;最后是一个后缀(?=\/\1),注意后缀里的\/用到了字符转义,\1则反向引用捕获的第1组即前面(\w+)匹配的内容,这样如果前缀是b的话,后缀就是/b了。于是,整个表达式匹配的是b和/b之间的内容(强调:不包括前缀和后缀本身)。例8(负向零宽断言,语法略)查找这样的单词:它里面出现了字母q,但是q后面跟的不是字母u:\b\w*q[^u]\w*\b此时有个问题:因为[^u]总要匹配一个字符,若q出现在单词的结尾的话,将会匹配q后面的单词分隔符(空格或句号或其它),那么接着后面的\w*\b将会匹配下一个单词,于是就能匹配整个Iraqfighting使用下列语句可避免:\b\w*q(?!u)\w*\b再例如,\d{3}(?!\d)——匹配3位数字,且这3位数字后面不能是数字\b((?!abc)\w)+\b——匹配不包含连续字符串abc的单词(?![a-z])\d{7}——匹配前面不是小写字母的7位数字例9(添加注释)语法:(?#comment)例如,IP地址2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)另外,要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,前文的一个表达式写成这样:(?=#断言要匹配的文本的前缀(\w+)#查找尖括号括起来的字母或数字(即HTML/XML标签))#前缀结束.*#匹配任意文本(?=#断言要匹配的文本的后缀\/\1#查找尖括号括起来的内容:前面是一个/,后面是先前捕获的标签)#后缀结束例10(贪婪与懒惰)通常匹配的原则是:匹配尽可能多的字符。例如a.*b将会匹配最长的以a开始,以b结束的字符串。若用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。有时需要的是懒惰匹配,即匹配尽可能少的字符:只需要在限定符后面加上?即可。例如,a.*?b将匹配最短的,以a开始,以b结束的字符串,若用它来搜索aabab的话,它会匹配aab和后面的ab懒惰限定符代码/语法说明*?重复任意次,但尽可能少重复+?重复1次或更多次,但尽可能少重复??重复0次或1次,但尽可能少重复{n,m}?重复n到m次,但尽可能少重复{n,}?重复n次以上,但尽可能少重复例11(处理选项,参考).Net中常用的处理选项名称说明IgnoreCase(忽略大小写)匹配时不区分大小写。Multiline(多行模式)更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.)Singleline(单行模式)更改.的含义,使它与每一个字符匹配(包括换行符\n)。.Net中常用的处理选项名称说明IgnorePatternWhitespace(忽略空白)忽略表达式中的非转义空白并启用由#标记的注释。ExplicitCapture(显式捕获)仅捕获已被显式命名的组。例12(平衡组——递归匹配嵌套的层次结构,堆栈思想)匹配括号,用代替:#最外层的左括号[^]*#最外层的左括号后面的不是括号的内容(((?'Open')#碰到了左括号,在黑板上写一个Open[^]*#匹配左括号后面的不是括号的内容)+((?'-Open')#碰到了右括号,擦掉一个Open[^]*#匹配右括号后面不是括号的内容)+)*(?(Open)(?!))#在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的Open;如果还有,则匹配失败#最外层的右括号一个常见的应用:匹配HTML中嵌套的div标签:div