解惑正则表达式中的捕获

读jQuery源码,其中不可避免的要弄明白正则表达式,在此对非捕获组(non-capturing)进行了一些梳理。

关于捕获的一些主要用法

  1. (?:X)
  2. (?=X)
  3. (?<=X)
  4. (?!X)
  5. (?<!X)

捕获

要书明白捕获,就要先从分组开始。重复单字符我们可以使用限定符,如果重复字符串,用什么呢? 对!用小括号,小括号里包裹指定字表达式(子串),这就是分组。之后就可以限定这个子表示的重复次数了。

那么,什么是捕获呢?使用小括号指定一个子表达式后,匹配这个子表达式的文本(即匹配的内容)可以在表达式或者其他过程中接着用,怎么用呢?至少应该有个指针啥的引用它吧? 对!默认情况下,每个分组(小括号)会自动

拥有一个组号,从左到右,以分组的左括号为标志,第一个出现的分组组号为1,后续递增。如果出现嵌套,

例如:

“aabcd”

采用正则 (a(b))(c) match 结果入下:

分组 捕获
$1(group1) ab
$2(group2) b
$3(group3) c

继续涨姿势。

一、(?:)非捕获组走起。

由下面一个例子引发对非捕获组的学习。

有两个金额:6000¥ 和 1000$。

需求是得到金额和货币种类。

『废话少说,多用正则』:

(\d+)+([$¥])

输出结果为:

OK,满足了要求。这里的正则分成了两个组,一个是(d+),一个是(¥$),前一个组($1)匹配金额,后一个组($2)匹配货币种类。

现在,需求变了!! 我需要这个正则同时可以匹配浮点数小数点前面的整数。如10010.86¥,提炼出 10010 和 ¥。

那么正则如下:

(\d+)(\.?)(\d)([$¥]) 

这里用括号分了四组,所以要输出金额的整数部分和种类,要分别输了$1,$4了。如果输出部分和正则是分开的,我希望只修改正则而不去修改输出部分的代码,也就是还是用$1,$2作为输出。由此可以引出非捕获组(?:)。

把前面的正则修改为:

(\d+)(?:\.?)(?:\d+)([¥$])$ 

这样,还是用$1,$2做为输出,同样输出了 10010 和 ¥

这个正则的中间两个组用到的就是非捕获组(?:),它可以理解为只分组而不捕获。

二、(?=)和(?<=) 前后查找

有的资料把它们叫做肯定式向前查找和肯定式向后查找;

有的资料也叫做肯定顺序环视和肯定逆序环视。

1、直接看下面的例子:

[0-9a-z]{2}(?=aa) var str = "12332aa438aaf"; 

Match List:

1 32
2 38

这个正则的意思是:匹配这么一个字符串,它要满足:是两位字符(数字,或字母),且后面紧跟着两个a。

分析一下:

32aa 这个子串满足这个条件,所以可以匹配到,又因为 (?=) 的部分是不捕获的,所以输出的只是 32,不包括aa。同理 38aa 也匹配这个正则,而输出仅是 38。

再深入看一下:

当str第一次匹配成功输出 32 后,程序要继续向后查找是否还有匹配的其它子串。那么这时应该从 32aa 的后一位开始向后查找,还是从 32 的后一位呢?也就是从索引 5 开始还是从 7 开始呢?有人可能想到是从 32aa 的下一位开始往后找,因为 32aa 匹配了正则,所以下一位当然是它的后面也就是从 4 开始。但实际上是从 32 的后一位也就是第一个 a 开始往后找。原因还是 (?=) 是非捕获的。

查阅API文档是这么注释的:

(?=X) X, via zero-width positive lookahead

可见zero-width(零宽度)说的就是这个意思。

现在,把字符串写的更有意思些:str = "aaaaaaaa";

看一下它的输出: aa aa aa

分析一下:

这个字符串一共有8个a。

第一次匹配比较容易找到,那就是前四个:aaaa ,当然第三和第四个 a 是不捕获的,所以输出是第一和第二个a;

接着继续查找,这时是从第三个a开始,三到六,这4个a区配到了,所以输出第三和第四个a;

接着继续查找,这时是从第五个a开始,五到八,这4个a区配到了,所以输出第五和第六个a;

接着往后查找,这时是从第七个a开始,显然,第七和第八个a,不满足正则的匹配条件,查找结束。

我们再延伸一下,刚说的情况的是(?=)放在捕获的字符串后面,它如果放在前面又是什么结果呢?

例子换成:

(?=hopeful)hope 

它的输出是hope。

正则的意思是:是否能匹配hopeful,如果能,则捕获hopeful中的hope。当然继续向后查找匹配的子串,是从f开始。

比较一下可以看出,(?=hopeful)hope 和 hope(?=ful),两个正则的效果其实是一样的。

2、下面说一下 (?<=)

把正则改一下,

(?<=aa)[0-9a-z]{2}; 

字符串还是str = "12332aa438aaf";

它的输出:43。

这个正则的意思是:匹配这么一个字符串,它要满足:是两位字符(数字或字母),且前面紧跟的是两个字母 a。

同样,深入一下,把str换成str = "aaaaaaaa";看一下输出是什么,同样也是:aa aa aa

分析一下:

第一次匹配不用说,是前四个a,输出的是第三和第四个a;

继续向后查找,从第五个a开始,程序发现,第五个和第六个a满足,因为是两位字符,且满足前面紧跟着两个a(第三和第四个a)。所以匹配成功,输出第五个和第六个a;

继续向后查找,从第七个a开始,程序发现,第七个和第八个a满足,因为是两位字符,且满足前面紧跟着两个a(第五和第六个a)。所以匹配成功,输出第七和第八个a。查找结束。

三、(?!)和(?<!) 逆袭!

从外观上看,和前面一组很相似,区别就是把 ‘=’ 换成了 ‘!’

那么意义刚好也是相反的。

[0-9a-z]{2}(?!aa)意思是:匹配两个字符,且后面紧跟着的不是aa

(?<!aa)[0-9a-z]{2} 意思是:匹配两个字符,且前面紧跟着的不是aa

用法和前面讲的差不多,这里不再详述。

解惑正则表达式中的捕获

时间: 2024-10-17 09:05:09

解惑正则表达式中的捕获的相关文章

Java正则表达式中的捕获组的概念(转)

要弄清这三个方法,首先要弄清Java正则表达式中的捕获组的概念.捕获组也就是Pattern中以括号对“()”分割出的子Pattern.至于为什么要用捕获组呢,主要是为了能找出在一次匹配中你更关心的部分.捕获组可以通过从左到右计算其开括号来编号.例如,在表达式 "(x)(y\\w*)(z)" 中,存在三个这样的组:  1.  x2.  y\\w*3.  z组零始终代表整个表达式.之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列.捕获的子序列稍后可以通过 Bac

正则表达式中的捕获和非捕获

之前我们在做例子的时候,我们看到过这种捕获的形式,我们也试过反向引用.那像我们捕获的内容,可以通过一个正则表达式的模式,或者部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储.缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式.每个缓冲区都可以使用 '\n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数.当然,如果我们不想捕获我们的文本,可以使用非捕获元字符 '?:'.'?=' 或 '?!' 来重写捕获

正则表达式中的符号含义

. 匹配除换行符以外的任意字符\w 匹配字母或数字或下划线或汉字\s 匹配任意的空白符\d 匹配数字\b 匹配单词的开始或结束^ 匹配字符串的开始$ 匹配字符串的结束[\u4e00-\u9fa5]{2,20} 匹配2-20个汉字* 重复零次或更多次+ 重复一次或更多次? 重复零次或一次{n} 重复n次{n,} 重复n次或更多次{n,m} 重复n到m次\W 匹配任意不是字母,数字,下划线,汉字的字符\S 匹配任意不是空白符的字符\D 匹配任意非数字的字符\B 匹配不是单词开头或结束的位置[^x]

计算机程序的思维逻辑 (89) - 正则表达式 (中)

上节介绍了正则表达式的语法,本节介绍相关的Java API. 正则表达式相关的类位于包java.util.regex下,有两个主要的类,一个是Pattern,另一个是Matcher.Pattern表示正则表达式对象,它与要处理的具体字符串无关.Matcher表示一个匹配,它将正则表达式应用于一个具体字符串,通过它对字符串进行处理. 字符串类String也是一个重要的类,我们在29节专门介绍过String,其中提到,它有一些方法,接受的参数不是普通的字符串,而是正则表达式.此外,正则表达式在Jav

正则表达式中各种字符的意义

正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串.将匹配的子串做替换或者从某个串中取出符合某个条件的子串等. 列目录时, dir *.txt或ls *.txt中的*.txt就不是一个正则表达式,因为这里*与正则式的*的含义是不同的. 正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式.正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配. 3.1 普通字符 由所有那些未显式指定为元字符的

正则表达式中各种字符的含义

正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串.将匹配的子串做替换或者从某个串中取出符合某个条件的子串等. 列目录时, dir *.txt或ls *.txt中的*.txt就不是一个正则表达式,因为这里*与正则式的*的含义是不同的. 正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式.正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配. 3.1 普通字符 由所有那些未显式指定为元字符的

正则表达式中各字符的含义

正则表达式中各种字符的含义 正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串.将匹配的子串做替换或者从某个串中取出符合某个条件的子串等. 列目录时, dir *.txt或ls *.txt中的*.txt就不是一个正则表达式,因为这里*与正则式的*的含义是不同的. 正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式.正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配. 3.1 普通字符 由

javascript正则表达式中 (?=exp)、(?&lt;=exp)、(?!exp)

 (?=exp) 百度百科给的解释:非获取匹配,正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用.例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”.预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始. 只看这个,看一辈子恐怕也看不明白. 我们看个案例: conso

[Z]正则表达式中的符号

. 匹配除换行符以外的任意字符 \w 匹配字母或数字或下划线或汉字 \s 匹配任意的空白符 \d 匹配数字 \b 匹配单词的开始或结束 ^ 匹配字符串的开始 $ 匹配字符串的结束 [\u4e00-\u9fa5]{2,20} 匹配2-20个汉字 * 重复零次或更多次 + 重复一次或更多次 ? 重复零次或一次 {n} 重复n次 {n,} 重复n次或更多次 {n,m} 重复n到m次 \W 匹配任意不是字母,数字,下划线,汉字的字符 \S 匹配任意不是空白符的字符 \D 匹配任意非数字的字符 \B 匹配