`
kabike
  • 浏览: 599272 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

正则表达式 捕获组,向前引用,零宽度断言,贪婪量词,惰性量词以及支配量词

阅读更多
捕获组
正则表达式中的括号相当于一个分组,比如下面这个正则表达式,就把字符串分成了"_"分割的三个分组,
然后可以利用$1引用第一个分组,$3引用第三个分组
		Pattern p = Pattern.compile("([^_]+)_(\\d+)_([^_]+)");
		String src1 = "孙燕姿_20091103_遇见.mp3";
		Matcher m = p.matcher(src1);
		System.out.println(m.replaceAll("$1_$3"));
		//output 孙燕姿_遇见.mp3


向前引用
假设想匹配字符串中的"hello hello"这样的字串是很容易的,但是想匹配所有的这样的重复字符串呢(比如 "aaa aaa"和"www www")?
可以利用向前引用,即寻找已经匹配的捕获组.
比如下面这个正则表达式,寻找这样的匹配模式:多个字母(形成了捕获组1),一个空格,捕获组1
		Pattern p = Pattern.compile("(\\w+)\\s\\1");
		String src = "I always make make some mistakes when when writting.";
		Matcher m = p.matcher(src);
		while (m.find()) {
			System.out.println(m.group());
		}
		//output make make
		 		 when when


零宽度断言
假设想提取字符串中的括号中的数字(不包括括号),其实可以用这样的模式
(\d+)
但是这样会把括号也包括到匹配结果中,还要最后去掉括号.这时可以考虑零宽度断言,零宽度断言就像一种判断,
比如下面这个正则表达式,匹配的模式为: 多个数字,并且这些数字之前是"(",并且这些数字之后是")"
		Pattern p = Pattern.compile("(?<=\\()\\d+(?=\\))");
		String src = "some useless word (497872028) some other crap 1321112232";
		Matcher m = p.matcher(src);
		while (m.find()) {
			System.out.println(m.group());
		}
		
		// output 497872028

这个可以有如下变体.提取括号以及@@之间的 数字(不包括括号和@@)
		Pattern p = Pattern.compile("(?<=\\(|@@)\\d+(?=\\)|@@)");
		String src = "some @@497872027@@ useless word (497872028) some other crap 1321112232";
		Matcher m = p.matcher(src);
		while (m.find()) {
			System.out.println(m.group());
		}
		
		// output 497872027
		// output 497872028	


贪婪量词和惰性量词
考虑这个例子,提取##之间的部分(包括#),很容易想
		Pattern p = Pattern.compile("#.+#");
		String src = "some #stupid# word and some crap";
		Matcher m = p.matcher(src);
		while (m.find()) {
			System.out.println(m.group());
		}
		// #stupid#

那么字符串改一下呢
		Pattern p = Pattern.compile("#.+#");
		String src = "some #stupid# word and some #crap#";
		Matcher m = p.matcher(src);
		while (m.find()) {
			System.out.println(m.group());
		}
		// #stupid# word and some #crap#

因为默认情况下正则表达式的量词(即那个+)是贪婪的,它尝试尽可能多的匹配.
这时可以试下惰性量词,在+后面加?,表示尽可能少的进行匹配.
		Pattern p = Pattern.compile("#.+?#");
		String src = "some #stupid# word and some #crap#";
		Matcher m = p.matcher(src);
		while (m.find()) {
			System.out.println(m.group());
		}
		// #stupid#
		// #crap#

其实不用惰性量词,用 #[^#]+# 也可以

支配量词
支配量词比较复杂,我也不是完全清楚,这里谈一些个人见解,
我感觉它工作起来像贪婪量词,但是它不回溯,因为支配量词丢弃原来的状态.
比如贪婪量词模式 是\\w+:
字符串是 hello,那么这个字符串是肯定不满足模式的,因为它末尾没有那个分号.
不过贪婪量词先用 \\w+匹配 hello,发现不成功,进行回溯,就是用\\w+匹配hell,依然失败,接着匹配hel.
直到完全失败为止.

支配量词模式为\\w++:
在\\w++匹配了hello之后,发现后面没有分号,匹配失败,要进行回溯.但是支配量词没有保留h he hel hell这几个中间状态,
所以无法回溯,直接失败.
因此支配量词在这种情况下理论上效率会高一些,因为它少了这些回溯中间状态的步骤.
先看贪婪量词
		Pattern p = Pattern.compile("\\w+:");
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i <= 40; i++) {
			sb.append("alonglongsentence");
		}

		String src = sb.toString();
		long start = System.currentTimeMillis();
		for (int i = 0; i <= 100; i++) {
			p.matcher(src).find();
		}

		System.out.println(System.currentTimeMillis() - start);
		//1391

下面用支配量词
		Pattern p = Pattern.compile("\\w++:");
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i <= 40; i++) {
			sb.append("alonglongsentence");
		}

		String src = sb.toString();
		long start = System.currentTimeMillis();
		for (int i = 0; i <= 100; i++) {
			p.matcher(src).find();
		}

		System.out.println(System.currentTimeMillis() - start);
		// 875

上面都是匹配失败的情况,下面试试成功的.因为成功不需要回溯,因此速度快了很多嘛.为了对比,我把字符串长度和匹配次数都变大了.
		Pattern p = Pattern.compile("\\w+:");
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i <= 400; i++) {
			sb.append("alonglongsentence");
		}
		sb.append(":");
		String src = sb.toString();
		long start = System.currentTimeMillis();
		for (int i = 0; i <= 1000; i++) {
			p.matcher(src).find();
		}

		System.out.println(System.currentTimeMillis() - start);
		// 250
		
		Pattern p = Pattern.compile("\\w++:");
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i <= 400; i++) {
			sb.append("alonglongsentence");
		}
		sb.append(":");
		String src = sb.toString();
		long start = System.currentTimeMillis();
		for (int i = 0; i <= 1000; i++) {
			p.matcher(src).find();
		}

		System.out.println(System.currentTimeMillis() - start);
		// 235

现在差别就不明显了.
关于贪婪量词和支配量词
我猜测字符串a12b  和模式a\d+c匹配过程如下
那个逗号表示匹配的位置

,a12b ,a\d+c
a,12b a,\d+c
a1,2b a,\d+c  记录回溯状态 a1,2b a\d+,c
a12,b a,\d+c  记录回溯状态 a12,b a\d+,c
b匹配\d失败,回溯状态为a12,b a\d+,c
b匹配c失败,回溯状态为a1,2b a\d+,c
2匹配c失败,没有状态可以回溯,匹配失败

换成支配量词
a12b  a\d++c

,a12b ,a\d++c
a,12b a,\d++c
a1,2b a,\d++c  记录回溯状态a1,2b a\d++,c
a12,b a,\d++c  记录回溯状态a12,b a\d++,c
b匹配\d失败,回溯状态为a12,b a\d++,c 丢弃其他回溯状态
b匹配c失败,没有状态可以回溯,匹配失败

看出来支配量词回溯步骤变少了.a b之间的数字越多,它比贪婪量词要回溯的就越少

关于捕获组和零宽度断言,后来又补充了一部分内容https://www.jianshu.com/p/31b13caa332c
1
9
分享到:
评论

相关推荐

    Python正则表达式标准库使用教程.pdf

    本文介绍了Python对于正则表达式的支持,包括正则表达式基础以及Python正则表达式标准库的完整介绍及使用示例。本文的内容不包括如何编写高效的正则表达式、如何优化正则表达式,这些主题请查看其他教程。   ...

    精通正则表达式~~~

    精通正则表达式第三版 搜集于网络 前言..........I 第1章:正则表达式入门.... 1 解决实际问题... 2 作为编程语言的正则表达式... 4 以文件名做类比... 4 以语言做类比... 5 正则表达式的知识框架... 6 对于...

    [精通正则表达式(第三版)]

    第 1 章 正则表达式入门 29 解决实际问题 30 作为编程语言的正则表达式 32 以文件名做类比 32 以语言做类比 33 正则表达式的思维框架 34 对于有部分经验的读者 34 Egrep元字符 36 行的起始和结束 36 字符组...

    正则表达式中包含三种元素分别为:量词、元字符、修饰符正则表达式中包含三种元素分别为:量词、元字符、修饰符

    正则表达式中包含三种元素分别为:量词、元字符、修饰符正则表达式中包含三种元素分别为,这是常用的很有用

    python正则表达式及使用正则表达式的例子

    正则表达式 正则表达用来匹配字符串 正则表达式匹配过程 依次拿出表达式和文本中的字符串进行比价 如果每个字符都能匹配,则匹配成功;一旦有匹配不成功的字符,则匹配失败 如果有量词和边界,则匹配过程稍微有些...

    正则表达式、分组、子匹配(子模式)、非捕获子匹配(子模式)

    前面我们知道正则表达式有很多元字符表示匹配次数(量词),都是可以重复匹配前面出现的单个字符次数。有时候,我们可能需要匹配一组多个字符一起出现的次数。这个时候,我们需要分组了。就是用小括号来括起这些字符...

    .NET正则表达式基础教程代码

    .NET正则表达式基础使用教程。该文件为Visual Studio 2013项目文件。里面有4个小项目,分别用于介绍正则表达式的字符组,量词,分组,断言。具体请参考我即将推出的博客教程。谢谢各位。

    正则表达式教程之模式修正符使用介绍

    本节会向大家介绍模式修正符的概念、模式修正符的构成,以及结合实例的模式修正符的演示,大家在学习完本节内容之后,就完全可以读懂正则表达式了。 什么是模式修正符? 1, 模式修正符就是几个字母,我们在每个正则...

    PHP 正则表达式效率 贪婪、非贪婪与回溯分析(推荐)

    先扫盲一下什么是正则表达式的贪婪,什么是非贪婪?或者说什么是匹配优先量词,什么是忽略优先量词,好吧,下面通过实例给大家介绍下PHP 正则表达式效率 贪婪、非贪婪与回溯分析,一起看看吧

    JavaScript正则表达式的贪婪匹配和非贪婪匹配

    非贪婪匹配就是尽可能少的匹配,用法就是在量词后面加上一个“?”,比如: "aaaaa".match(/a+?/); //["a", index: 0, input: "aaaaa"] 但是非贪婪匹配有时候和我们期待的并不一样,比如: "aaab".match(/a+b/); ...

    正则表达式教程

    本教程主要参考自网上最普遍的《正则表达式30分钟入门教程》(http://deerchao.net/tutorials/regex/regex.htm),这是一个非常优秀的教程,深入浅出讲解了正则表达式的基本概念,更加深入的内容可以参考CSDN上过客...

    正则表达式分组实例详解

    正则表达式分组: 如果想匹配3个数字,正则表达式可以用如下写法: \d{3} 以上代码使用重复量词可以匹配3位数字。 但是在实际应用中,往往需要重复多个字符,例如我想重复ab两个字符,使用以下代码就不合适了,代码...

    PHP和正则表达式教程集合之二第1/2页

    正则表达式快速入门(二) 【导读】在本文里,我们主要介绍子模式(subpatterns),逆向引用(Back references)和量词(quantifiers) 在上篇文章里,我们介绍了正则表达式的模式修正符与元字符,细心的读者也许会发现,...

    Python 正则表达式完整示例教程

    文章目录正则表达式元字符基本元字符:预定义字符使用量词贪婪量词字符分组反向引用分组非捕获分组re模块介绍search()和match()函数match对象的方法findall()和finditer()字符串分割和替换编译正则表达式 正则表达式...

    正则表达式之 贪婪与非贪婪模式详解(概述)

    1 概述 贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为,贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配,而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配。非贪婪模式只被部分NFA引擎...

    正则表达式{n,m}量词(至少n次,最多m次)

    正则表达式{n,m}量词: {n,m}量词可以重复前面匹配的字符n-m次,至少n次,最多m次。 语法结构: 构造函数方式: new RegExp("x{n,m}") 对象直接量方式: /x{n,m}/ 浏览器支持: IE浏览器支持此元字符。 火狐浏览器支持...

    javascript 进阶篇1 正则表达式,cookie管理,userData

    首先,什么事正则表达式呢,其实引入概念很多时候并不能帮我们明白它到底是什么,所以我先简单描述下,正则表达式,其实就是一个记录字符串规则则的字符串,等我们看完这一部分,也就能明白它到底是什么了。...

    编写高质量的js之正确理解正则表达式回溯

    当一个正则表达式扫描目标字符串时,从左到右逐个扫描正则表达式的组成部分,在每个位置上测试能不能找到一个匹配。对于每一个量词和分支,都必须确定如何继续进行。如果是一个量词(如*、+?或者{2,}),那么正则...

    正则表达式

    正则表达式中包含两种元素分别为:量词、元字符,是java,html,java web学习的资料。

    正则表达式+Python re模块详解

    正则表达式(Regluar Expressions)又称规则表达式,在代码中常简写为REs,regexes或regexp(regex patterns)。它本质上是一个小巧的、高度专用的编程语言。 通过正则表达式可以对指定的文本实现 匹配测试、内容...

Global site tag (gtag.js) - Google Analytics