微软的正则表达式教程(五):选择/编组和后向引用

原创 2004年06月30日 14:05:00

选择与编组

选择允许使用 '|' 字符来在两个或多个候选项中进行选择。通过扩展章节标题的正则表达式,可以将其扩充为不仅仅适用于章节标题的表达式。不过,这可没有想象的那么直接。在使用选择时,将匹配'|' 字符每边最可能的表达式。你可能认为下面的 JScript 和 VBScript 表达式将匹配位于一行的开始和结束位置且后跟一个或两个数字的 'Chapter' 或 'Section':

/^Chapter|Section [1-9][0-9]{0,1}$/ "^Chapter|Section [1-9][0-9]{0,1}$"

不幸的是,真正的情况是上面所示的正则表达式要么匹配位于一行开始处的单词 'Chapter',要么匹配一行结束处的后跟任何数字的 'Section'。如果输入字符串为 'Chapter 22',上面的表达式将只匹配单词 'Chapter'。如果输入字符串为 'Section 22',则该表达式将匹配 'Section 22'。但这种结果不是我们此处的目的,因此必须有一种办法来使正则表达式对于所要做的更易于响应,而且确实也有这种方法。

可以使用圆括号来限制选择的范围,也就是说明确该选择只适用于这两个单词 'Chapter' 和 'Section'。不过,圆括号同样也是难处理的,因为它们也用来创建子表达式,有些内容将在后面关于子表达式的部分介绍。通过采用上面所示的正则表达式并在适当位置添加圆括号,就可以使该正则表达式既可以匹配 'Chapter 1',也可以匹配 'Section 3'。

下面的正则表达式使用圆括号将 'Chapter' 和 'Section' 组成一组,所以该表达式才能正确工作。对 JScript 为:

/^(Chapter|Section) [1-9][0-9]{0,1}$/

对 VBScript 为:

"^(Chapter|Section) [1-9][0-9]{0,1}$"

这些表达式工作正确,只是产生了一个有趣的副产品。在 'Chapter|Section' 两边放置圆括号建立了适当的编组,但也导致两个待匹配单词之一都被捕获供今后使用。由于在上面所示的表达式中只有一组圆括号,因此只能有一个捕获的 submatch。可以使用 VBScript 的Submatches 集合或者JScript 中RegExp 对象的 $1-$9 属性来引用这个子匹配。

有时捕获一个子匹配是所希望的,有时则是不希望的。在说明所示的示例中,真正想做的就是使用圆括号对单词 'Chapter' 或 'Section' 之间的选择编组。并不希望在后面再引用该匹配。实际上,除非真的是需要捕获子匹配,否则请不要使用。由于不需要花时间和内存来存储那些子匹配,这种正则表达式的效率将更高。

可以在正则表达式模式圆括号内部的前面使用 '?:'来防止存储该匹配供今后使用。对上面所示正则表达式的下述修改提供了免除子匹配存储的相同功能。对 JScript:

/^(?:Chapter|Section) [1-9][0-9]{0,1}$/

对 VBScript:

"^(?:Chapter|Section) [1-9][0-9]{0,1}$"

除了 '?:' 元字符,还有两个非捕获元字符用于称之为预查的匹配。一个为正向预查,用 ?= 表示, 在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串。一个为负向预查,用 '?!' 表示,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。

例如,假定有一个包含引用有 Windows 3.1、Windows 95、Windows 98 以及 Windows NT 的文档。进一步假设需要更新该文档,方法是查找所有对 Windows 95、Windows 98 以及 Windows NT 的引用,并将这些引用更改为 Windows 2000。可以使用下面的 JScript 正则表达式,这是一个正向预查,来匹配 Windows 95、Windows 98 以及 Windows NT:

/Windows(?=95 |98 |NT )/

在 VBScript 要进行同样的匹配可以使用下述表达式:

"Windows(?=95 |98 |NT )"

找到一个匹配后,紧接匹配到的文字(而不包括预查中使用的字符)就开始对下一次匹配的搜索。例如,如果上面所示的表达式匹配到 'Windows 98',则将从 'Windows' 而不是 '98' 之后继续查找。

后向引用

正则表达式一个最重要的特性就是将匹配成功的模式的某部分进行存储供以后使用这一能力。请回想一下,对一个正则表达式模式或部分模式两边添加圆括号将导致这部分表达式存储到一个临时缓冲区中。可以使用非捕获元字符 '?:', '?=', or '?!' 来忽略对这部分正则表达式的保存。

所捕获的每个子匹配都按照在正则表达式模式中从左至右所遇到的内容存储。存储子匹配的缓冲区编号从 1 开始,连续编号直至最大 99 个子表达式。每个缓冲区都可以使用 '/n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

后向引用一个最简单,最有用的应用是提供了确定文字中连续出现两个相同单词的位置的能力。请看下面的句子:

Is is the cost of of gasoline going up up?

根据所写内容,上面的句子明显存在单词多次重复的问题。如果能有一种方法无需查找每个单词的重复现象就能修改该句子就好了。下面的 JScript 正则表达式使用一个子表达式就可以实现这一功能。

//b([a-z]+) /1/b/gi

等价的 VBScript 表达式为:

"/b([a-z]+) /1/b"

在这个示例中,子表达式就是圆括号之间的每一项。所捕获的表达式包括一个或多个字母字符,即由'[a-z]+' 所指定的。该正则表达式的第二部分是对前面所捕获的子匹配的引用,也就是由附加表达式所匹配的第二次出现的单词。'/1'用来指定第一个子匹配。单词边界元字符确保只检测单独的单词。如果不这样,则诸如 "is issued" 或 "this is" 这样的短语都会被该表达式不正确地识别。

在 JScript 表达式中,正则表达式后面的全局标志 ('g') 表示该表达式将用来在输入字符串中查找尽可能多的匹配。大小写敏感性由表达式结束处的大小写敏感性标记 ('i') 指定。多行标记指定可能出现在换行符的两端的潜在匹配。对 VBScript 而言,在表达式中不能设置各种标记,但必须使用 RegExp 对象的属性来显式设置。

使用上面所示的正则表达式,下面的 JScript 代码可以使用子匹配信息,在一个文字字符串中将连续出现两次的相同单词替换为一个相同的单词:

var ss = "Is is the cost of of gasoline going up up?./n"; var re = //b([a-z]+) /1/b/gim; //创建正则表达式样式. var rv = ss.replace(re,"$1"); //用一个单词替代两个单词.

最接近的等价  VBScript 代码如下:

Dim ss, re, rv ss = "Is is the cost of of gasoline going up up?." & vbNewLine Set re = New RegExp re.Pattern = "/b([a-z]+) /1/b" re.Global = True re.IgnoreCase = True re.MultiLine = True rv = re.Replace(ss,"$1")

请注意在 VBScript 代码中,全局、大小写敏感性以及多行标记都是使用 RegExp 对象的适当属性来设置的。

replace 方法中使用 $1 来引用所保存的第一个子匹配。如果有多个子匹配,则可以用 $2, $3 等继续引用。

后向引用的另一个用途是将一个通用资源指示符 (URI) 分解为组件部分。假定希望将下述的URI 分解为协议 (ftp, http, etc),域名地址以及页面/路径:

http://msdn.microsoft.com:80/scripting/default.htm

下面的正则表达式可以提供这个功能。对 JScript,为:

/(/w+):////([^/:]+)(:/d*)?([^# ]*)/

对 VBScript 为:

"(/w+):////([^/:]+)(:/d*)?([^# ]*)"

第一个附加子表达式是用来捕获该 web 地址的协议部分。该子表达式匹配位于一个冒号和两个正斜杠之前的任何单词。第二个附加子表达式捕获该地址的域名地址。该子表达式匹配不包括 '^'、 '/' 或 ':' 字符的任何字符序列。第三个附加子表达式捕获网站端口号码,如果指定了该端口号。该子表达式匹配后跟一个冒号的零或多个数字。最后,第四个附加子表达式捕获由该 web 地址指定的路径以及/或者页面信息。该子表达式匹配一个和多个除'#' 或空格之外的字符。

将该正则表达式应用于上面所示的 URI 后,子匹配包含下述内容:

RegExp.$1 包含 "http"

RegExp.$2 包含 "msdn.microsoft.com"

RegExp.$3 包含 ":80"

RegExp.$4 包含 "/scripting/default.htm"

正则表达式后向引用详解

http://c.biancheng.net/cpp/html/1413.html 使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进...
  • z69183787
  • z69183787
  • 2016年08月20日 22:42
  • 1385

正则表达式(后向引用和断言)

后向引用 使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的...
  • hsany330
  • hsany330
  • 2015年02月05日 18:18
  • 1142

正则表达式详解(贪婪与懒惰、前瞻与后顾、后向引用等)

之前嫌正则麻烦,一直没有深入去了解过正则,能不用的地方就不使用。 最近项目中遇到了不可避免的正则使用,所以花了点时间去了解并整理了一下,理解不一定完全准确,如有不对欢迎指出,希望对大家有所帮助。 ...
  • u013197629
  • u013197629
  • 2017年06月20日 15:45
  • 821

正则表达式之分组、后向引用

分组          正则表达式中的分组又称为子表达式,就是把一个正则表达式的全部或部分当做一个整体进行   处理,分成一个或多个组。其中分组是使用“()”表示的。进行分组之后“()”里面...
  • chenlei0630
  • chenlei0630
  • 2014年02月10日 14:16
  • 3431

微软的正则表达式教程(五)

微软的正则表达式教程(五):选择/编组和后向引用 选择与编组选择允许使用 | 字符来在两个或多个候选项中进行选择。通过扩展章节标题的正则表达式,可以将其扩充为不仅仅适用于章节标题的表达式。不过,这可没...
  • ismezy2002
  • ismezy2002
  • 2006年11月27日 12:56
  • 526

python里使用正则表达式的组匹配通过名称自引用

在前学习过正则表达式的组可以通过组号来自引用,看起来使用很简单的样子,其实它还是不容易维护的,比如你某一天需要在这个正则表达式里插入一个组时,就发现后面的组号全打乱了,因此需要一个一个地更改组号,有没...
  • caimouse
  • caimouse
  • 2017年11月14日 18:10
  • 378

正则:后向引用文本替换

正则表达式的三部曲应该是:1、查找;2、引用匹配了的文本(后向引用);3、有选择地替换文本。 需要注意的是:大部分语言的正则表达式实现,在查找中,使用后向引用来代表一个子模式,其语法是“\数字”...
  • hsany330
  • hsany330
  • 2015年02月05日 14:55
  • 468

sed配合正则表达式应用案例

问题:利用linux正则表达式把IP取出来。 解答:[root@ianLinux ~]# ifconfig eth0|sed -n '2p' inet addr:192.168.0.199 Bca...
  • codeTZ
  • codeTZ
  • 2016年09月04日 18:38
  • 1264

正则表达式 进阶(二)-- 回溯引用、前后查找、嵌入条件

继续讲正则表达式的高级形式的应用,这也是最后一篇正则的文章,希望本博客的3篇正则的文章能够让大多数朋友了解正则的使用规则,并希望各位能够多多练习,提高自己的工作效率。 如果不熟悉正则或者不太记得一些...
  • wzzfeitian
  • wzzfeitian
  • 2013年05月26日 21:00
  • 8835

正则表达式:后向引用

预备知识replace()方法大家一定很熟悉了吧,stringObject.replace(regexp/substr,replacement)w3school里面有一段话相信大家也看过了: 注意...
  • qq_31070475
  • qq_31070475
  • 2017年10月07日 16:42
  • 93
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:微软的正则表达式教程(五):选择/编组和后向引用
举报原因:
原因补充:

(最多只允许输入30个字)