多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

正则表达式——前后查找

正则表达式——前后查找 前后查找1、前后查找2、向前查找3、向后查找4、把向前查找和向后查找结合起来5、对前后查找取非1、前后查找我们还是先来看一个例子你要把一个Web页面的页面标题提取出来。HTML页面标题是出现在TITLE和/TITLE标签之间的文字而这对标签又必须嵌在HTML代码的HEAD部分里。下面就是这个例子文本HEADTITLEBen Fotas Homepage/TITLE/HEAD正则表达式[tT][iI][tT][lL][eE].*\/[tT][iI][tT][lL][eE]结果[tT]​[iI]​[tT]​[lL]​[eE].*/[tT]​[iI]​[tT]​[lL]​[eE]匹配标签大写、小写或大小写混用​、/TITLE标签以及这两个标签之间的任何文字。这个模式的效果与我们的预期基本相符但不够理想。为什么这么说呢因为只有页面标题才是我们需要的而我们找到的匹配里还包含着TITLE和/TITLE标签。能不能只返回页面标题的文字部分呢办法之一是使用子表达式。我们可以利用子表达式把被匹配文本划分为3个部分开始标签、标题文字、结束标签。把被匹配文本划分为多个部分之后从它们当中提取且只提取出我们需要的东西就很容易了。可是明知是自己并不真正需要的东西比如上例中的TITLE和/TITLE标签​还把它们检索出来岂不是毫无意义。​“先想办法把它们检索出来、再以手动方式排除它们”这既浪费时间又容易招致不必要的后患。在遇到这类问题的时候你真正需要的是这样一个模式它包含的匹配本身并不返回而是用于确定正确的匹配位置它并不是匹配结果的一部分。换句话说你需要进行“前后查找”。本章将对向前查找lookahead和向后查找lookbehind都进行讨论。常见的正则表达式实现都支持前者但支持后者的就没那么多了。Java、.NET、PHP和Perl都支持向后查找但有一些限制, JavaScript和ColdFusion不支持向后查找。2、向前查找向前查找指定了一个必须匹配但不在结果中返回的模式。向前查找实际就是一个子表达式而且从格式上看也确实如此。从语法上看一个向前查找模式其实就是一个以?开头的子表达式需要匹配的文本跟在的后面。我们来看一个例子。例子里的原始文本是一些URL地址而你的任务是把它们的协议名部分提取出来为下一步处理做准备​。下面就是这个例子文本http://www.forta.com/https://mail.forta.com/ftp://ftp.forta.com/正则表达式.(?:)结果在上面列出的URL地址里协议名与主机名之间以一个分隔。模式匹配任意文本第1个匹配是http​子表达式? :匹配​。注意被匹配到的并没有出现在最终的匹配结果里我们用向正则表达式引擎表明只要找到就行了不要把它包括在最终的匹配结果里——用术语来说就是“不消费”它。为了更好地理解的作用我们再来看一个同样的例子但这次不使用向前查找元字符正则表达式.(:)结果子表达式:正确地匹配到了并消费了这个字符——出现在了最终的匹配结果里。这两个例子的区别是前一个用来匹配的模式是?:​后一个用来匹配的模式是:​。这两个模式所匹配的东西是一样的——都是紧跟在协议名后面的那个​它们之间的区别只是被匹配到的字符有没有出现在最终的匹配结果里而已。在使用向前查找的时候正则表达式分析器将向前查找并处理匹配但不会把它包括在最终的搜索结果里。模式:查找到并且匹配结果包含​模式? :查找到但匹配结果不包含​。注意 向前查找和向后查找匹配本身其实是有返回结果的只是这个结果的字节长度永远是0而已。因此前后查找操作有时也被称为零宽度zero-width匹配操作。任何一个子表达式都可以转换为一个向前查找表达式只要给它加上一个前缀即可。在同一个搜索模式里可以使用多个向前查找表达式它们可以出现在模式里的任意位置而不仅仅是出现在整个模式的开头——就像你们在上面看到的那样​。3、向后查找正如你刚看到的那样?将向前查找查找出现在被匹配文本之后的字符但不消费那个字符​。因此?被称为向前查找操作符。除了向前查找许多正则表达式实现还支持向后查找也就是查找出现在被匹配文本之前的字符但不消费它​向后查找操作符?。?与?的具体使用方法大同小异它必须用在一个子表达式里而且后跟要匹配的文本。下面是一个例子。你从某个数据库里搜索出了一份产品目录但你只需要把那些产品的价格提取出来文本ABC01:$23.45HGG42:$5.31CFMX1:$899.00XTC99:$69.96Total items found:4正则表达式\$[0-9.]结果\$匹配$,[0-9.]匹配价格。如上所示的匹配结果符合你的预期。但如果你不想让$出现在最终的匹配结果里你该怎么办从这个模式里简单地把$去掉能达到目的吗正则表达式[0-9.]结果这显然不是你想要的结果。你需要\$来确定应该匹配哪些文本你只是不想让$出现在最终的匹配结果里而已。怎么办好办这正是向后查找可以大显身手的地方如下所示正则表达式(?\$)[0-9.]结果问题迎刃而解了。​? \$只匹配$但不消费它最终的匹配结果里只有价格数字没有前缀的$字符​。警告 向前查找模式的长度是可变的它们可以包含和之类的元字符所以它们非常灵活。而向后查找模式只能是固定长度——这是一条几乎所有的正则表达式实现都遵守的限制。4、把向前查找和向后查找结合起来向前查找和向后查找可以组合在一起使用就像下面这个例子所演示的那样这个例子解决了我们在本章刚开始时提出的问题​文本HEADTITLEBen Fotas Homepage/TITLE/HEAD正则表达式(?[tT][iI][tT][lL][eE]).*(?\/[tT][iI][tT][lL][eE])结果问题解决了。​? [tT]​[iI]​[tT]​[lL]​[eE]是一个向后查找操作它匹配但不消费TITLE而? \/[tT]​[iI]​[tT]​[lL]​[eE]是一个向前查找操作它匹配但不消费/TITLE。最终返回的匹配结果包含且仅包含标题文字用术语来说就是只有标题文字被消费了​。5、对前后查找取非到目前为止正如你看到的那样向前查找和向后查找通常用来匹配文本其目的是为了确定将被返回为匹配结果的文本的位置通过指定匹配结果的前后必须是哪些文本​。这种用法被称为正向前查找positivelookahead和正向后查找positive lookbehind​。术语“正”指的是寻找匹配的事实。前后查找还有一种不太常见的用法叫做负前后查找negative lookaround。负向前查找negativelookahead将向前查找不与给定模式相匹配的文本负向后查找negative lookbehind将向后查找不与给定模式相匹配的文本。我们曾经介绍过一个用来对字符集合进行取非处理的操作符^但^不能用来对前后查找进行取非处理。这里必须使用另外一种语法前后查找必须用来取非它将替换掉​。下表列出了所有的前后查找操作符。一般来说凡是支持向前查找的正则表达式实现都同时支持正向前查找和负向前查找。类似地凡是支持向后查找的正则表达式实现都同时支持正向后查找和负向后查找。为了演示正向后查找和负向后查找之间的区别我们来看一个例子。下面是一段包含着一些数值的文本其中既有价格又有数量。我们先来查找且只查找价格文本Ipaid $30for100apples,50oranges,and60pears.Isaved $5 onthisorder.正则表达式(?\$)\d结果这与我们刚见过的例子非常相似。\d匹配数值一个或多个数字字符,? \$向后查找但不消费字符$这个字符在模式里被转义为$​。这个模式正确地匹配到了两个用来表示价格的数值那些用来表示数量的数字没有出现在最终的匹配结果里。接下来我们再去查找且只查找数量正则表达式\b(?!\$)\d\b结果\d还是匹配数值但这次只匹配数量不匹配价格。表达式? ! \$是一个负向后查找它使得最终的匹配结果只包含那些不以$开头的数值。把操作符改为操作符使得整个模式从一个正向后查找变成了一个负向后查找。在上面这个例子里我们还在那个负向后查找模式里用\b元字符定义了两个单词边界。我们为什么要那么做呢你看过下面这个没有使用单词边界的例子里就明白了。正则表达式(?!\$)\d结果请看因为没有使用单词边界$30里的0也出现在了最终的匹配结果里。这是因为那个0字符的前一个字符是3而不是$它完全符合模式? ! $\d的匹配要求。把这个模式用\b括起来从根本上解决了这个问题。
返回列表