正则表达式的贪婪模式、非贪婪模式、占有模式
文章目录
- 一、Greediness(贪婪型)
- (一)贪婪模式示例
- (二)贪婪模式的匹配过程(贪婪模式的回溯)
- 二、Reluctant(勉强型)
- (一)非贪婪模式示例
- (二)非贪婪模式的匹配过程
- 三、Possessive(占有型)
- (一)占有模式示例
- (二)占用模式的匹配过程
一、Greediness(贪婪型)
贪婪模式,最大匹配,匹配优先。
在 Greediness
的模式下,会尽量大范围的匹配,直到匹配了整个内容,这时发现匹配不能成功时,开始回退缩小匹配范围,直到匹配成功。
默认情况下,所有的限定符都是贪婪模式,表示尽可能多的去捕获字符。
(一)贪婪模式示例
正则表达式:/<.+>/
,这个正则表达式的含义:匹配以 <
为首,以 >
为尾,中间是 1
个或多个任意字符的字符串。
被查找的字符串:a<tr>aava </tr>abb
匹配的结果:<tr>aava </tr>
Java 代码举例如下:
String test = "a<tr>aava </tr>abb";
String reg = "<.+>";
System.out.println(test.replaceAll(reg, "###"));
说明:上面的 Java 代码会将字符串 a<tr>aava </tr>abb
中所有符合正则式的字符串替换为 ###
,最后输出结果为:a###abb
。其实 <tr>
和</tr>
也符合正则式 /<.+>/
,但是贪婪呀,因为被匹配的字符串还很长,所以会继续匹配下去,直到捕获到最多且匹配成功为止。
注意:正则表达式的限定符(量词)?
、+
、*
以及区间限定符 {n,m}
默认都是贪婪模式。
再举个例子:
String test = "foooood";
String reg ="o{1,}";
System.out.println(test.replaceAll(reg, "#"));
上面的 Java 代码会一次匹配全部的字符 o
,什么意思?就是说整个正则表达式单次匹配成功中,会匹配尽可能多的字符,而 ooooo
是符合正则式 o{1,}
的,所以在单次匹配成功中就全部捕获了,所以最后输出的结果为:f#d
。如果你想得到这样的结果:f#####d
,应该怎么写表达式呢?那么表达式成功匹配一个 o
时,就算整个正则表达式匹配成功 1 次,那么成功匹配 5 个 o
,就算整个正则式匹配成功了 5 次,这样就会执行 5 次替换,最后 ooooo
就会替换成 #####
,我们看下面的示例。
String test = "foooood";
String reg ="o{1,}?";
System.out.println(test.replaceAll(reg, "#"));
在区间量词后添加个问号 ?
,就是非贪婪模式,那么匹配了一个 o
,就算正则式成功匹配一次,然后正则式又重新从正则式的第一个子表达式对被匹配的字符串已经成功匹配的下个字符开始继续匹配,直到无字符可匹配为止。
(二)贪婪模式的匹配过程(贪婪模式的回溯)
贪婪模式的正则表达式<.*>去匹配字符串"a<>aava</>abb",会匹配到"<>aava</>",下面说一下贪婪模式的具体匹配过程。
首先 <.*>
中的 <
获得控制权,尝试匹配字符串"a<>aava</>abb"中的第一个字符"a",匹配失败。继续尝试匹配第二个字符"<",匹配成功,将控制权交给 .*
。
.*
为贪婪模式,会尽可能多的匹配字符,会先去匹配第三个字符">",匹配成功,记录下一个回溯状态,继续匹配第四个字符"a",直到匹配到字符串结尾的最后一个字符"b"。字符串后没有字符了,.*
继续匹配失败,将控制权交给 >
。
>
尝试匹配字符串结尾失败,向前查找可回溯状态,控制权交给 .*
,.*
让出一个字符,也就是字符串结尾的"b",然后将控制权交给 >
。>
尝试匹配字符串结尾的"b",匹配失败,再次向前查找可回溯状态,将控制权交给 .*
…
重复以上过程,直到 .*
让出第十个字符">",>
匹配第十个字符">"成功,从而整个正则表达式匹配成功一次。<
继续从第十一个字符"a"开始匹配,但是直到字符串结束都没有匹配成功的,最终匹配结束。
可以看到,.*
会尽可能多的匹配字符,直到无法继续匹配,才会将控制权交给接下来的表达式。当接下来的表达式匹配失败后,.*
会让出之前匹配的字符,直到整个正则表达式匹配成功。
总结:贪婪模式的回溯,会让出已经匹配成功的字符给下一个正则式的表达式。吃太多了,别人没得吃,一点点吐出去给别人吃~
二、Reluctant(勉强型)
也叫 Laziness
,懒惰型,懒惰模式,勉强模式,非贪婪模式,最小匹配,忽略优先。
在 Reluctant
的模式下,就是在匹配成功的前提下,尽可能少的匹配字符。
在限定词后增加 ?
,则是非贪婪模式,表示尽可能少的去捕获字符。
例如:??
,*?
,+?
,{n,}?
,{n,m}?
(一)非贪婪模式示例
被查找的字符串:a<tr>aava </tr>abb
你希望匹配结果为:<tr>
,就要使用表达式:/<.+?>/
,但是从被查找的字符串可以知道,有两个地方匹配正则表达式,所以最终会匹配得到两个结果:<tr>
和 </tr>
。
Java 代码举例如下:
String test = "a<tr>aava </tr>abb";
String reg = "<.+>";
System.out.println(test.replaceAll(reg, "###"));
上面的 Java 代码会将字符串 a<tr>aava </tr>abb
中所有符合表达式的字符串替换为 ###
,最后输出结果为:a###aava ###abb
。
(二)非贪婪模式的匹配过程
先举个例子:
正则式 <.+?>
去匹配字符串 a<>aava</>abb
,会成功匹配到谁呢?四个选项:
A.<>
B.</>
C.<>aava</>
D.匹配失败
我相信很多人会选中选项 B,不过正确答案是 C。
想要理解为什么在非贪婪模式下,<.+?>
还是匹配了这么多字符,需要知道非贪婪模式下的匹配过程。
首先 <.+?>
中的 <
获得控制权,匹配字符串中的第一个字符"a",匹配失败。<
继续尝试匹配第二个字符"<",匹配成功。
接下来 .+?
获得控制权,.
可以匹配除换行符之外的任意一个字符,+
表示一个或多个,?
表示非贪婪模式,即在成功的前提下尽量少的匹配字符,所以 .+?
会先去匹配成功一次,然后立刻就会将控制权交给正则表达式中的下一个。如果是贪婪模式,.+
是不会匹配成功就早早地让出控制权的,而是会继续匹配下去,直到匹配失败了才把控制权让给正则表达式的下一个字符。
所以 .+?
获得控制权后,会先去匹配第三个字符">",成功后记录下回溯状态,立即将控制权交给表达式中的 >
。>
获得控制权后,会尝试匹配第四个字符"a",匹配失败,向前查找可回溯状态,于是 .+?
重新取得控制权,.+?
记录的回溯位置是第 3 个字符“>”,于是 .+?
会去匹配回溯位置的下一个字符,也就是第 4 个字符“a”,匹配成功后再次记录下回溯状态,然后再次将控制权交给 >
。>
获得控制权后,会尝试匹配第五个字符"a",结果匹配失败,再次向前查找可回溯状态…
就这样重复上述过程,直到 .+?
匹配到了第九个字符"/",将控制权交给 >
。>
匹配第十个字符">",终于匹配成功了,从而整个正则表达式匹配成功一次,记录下匹配结果。因为每个字符只能被正则匹配一次,所以 <
继续从第十一个字符"a"开始匹配,但是直到字符串结束都没有匹配成功的,最终匹配结束。
可以看到 .+?
会先尽可能少的匹配字符,优先将控制权交给接下来的表达式。但因为接下来的表达式一直匹配失败,.+?
不得不继续匹配字符。最终 .+?
的匹配内容是">aava</",并且进行了 6
次回溯。
总结:非贪婪模式的回溯,是被迫继续匹配下一个字符,再让出控制权。给别人吃,别人吃不了,只好自己吃掉。
三、Possessive(占有型)
占有模式,完全匹配模式。
在限定符后面添加 +
就成为“占有模式”,例如:?+
、*+
、++
、{n,}+
、{n,m}+
Possessive
模式与 Greediness
有一定的相似性,那就是都尽量匹配最大范围的内容,直到内容结束,但与 Greediness
不同的是,完全匹配不再回退尝试匹配更小的范围,也就是“不回溯”。
(一)占有模式示例
String test = "a<tr>aava </tr>abb";
String test2 = "<tr>";
String reg = "<.++>";
System.out.println(test.replaceAll(reg, "###"));
System.out.println(test2.replaceAll(reg, "###"));
以上的代码,输出结果为:a<tr>aava </tr>abb
和 <tr>
,也就是说字符串a<tr>aava </tr>abb
和 <tr>
都不符合正则表达式 <.++>
,那么这个表达式到底是怎么匹配的呢?
(二)占用模式的匹配过程
接下来看不回溯的占有模式是怎么匹配的。
占有模式的正则表达式 <.*+>
去匹配字符串"a<>aava</>abb",会匹配失败。具体的匹配过程如下。
前两步与贪婪模式的 <.*>
匹配过程基本一致。
<
获得控制权,尝试匹配字符串"a<>aava</>abb"中的第一个字符"a",匹配失败。继续尝试匹配第二个字符"<",匹配成功,将控制权交给 .*+
。
.*+
也会尽可能多的匹配字符。不同的是,占有模式下表达式并不会记录回溯状态。.*+
匹配到字符串结尾字符"b"后,没有字符了,于是继续匹配失败,将控制权交给 >
。
>
获得控制权后,尝试匹配字符串结尾,结尾无字符,匹配失败。因为没有回溯状态可查找,>
直接匹配失败,从而整个正则表达式 <.*+>
匹配失败。
带区间的类似 .{m,n}+
也是不带回溯状态的,需要注意,具体匹配过程类似上文,就不详述了。
总结:把东西全部吃掉了,也不吐出来,结果导致别人没得吃
正则表达式的贪婪模式、非贪婪模式、占有模式相关推荐
- 正则表达式之 贪婪与非贪婪模式详解
1 概述 贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为,贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配,而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配.非贪婪模式只被部分NF ...
- 转:正则表达式之 贪婪与非贪婪模式详解
1 概述 贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为,贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配,而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配.非贪婪模式只被部分NF ...
- 正则表达式口诀及常用正则 。正则贪婪与非贪婪模式
正则表达式口诀及常用正则 签于网上太多的介绍都是一篇凶悍的短文,边看边理解可以,帮助记忆不行.又受五笔字型字根表口诀"白手看头三二斤..."的启发, 试作"正则表达式助 ...
- python 正则表达式 re (.*?)和(.*)的区别(贪婪模式非贪婪模式)惰性匹配
贪婪模式 .* 非贪婪模式 .*? 1. .匹配任意除换行符"\n"外的字符: 2. *表示匹配前一个字符0次或无限次: 3. +或*后跟?表示非贪婪匹配,即尽可能少的匹配,如*? ...
- 正则表达式贪婪与非贪婪模式
正则表达式贪婪与非贪婪模式 之前做程序的时候看到过正则表达式的贪婪与非贪婪模式,今天用的时候就想不起来了,现在这里总结一下,以备自己以后用到注意. 1.什么是正则表达式的贪婪与非贪婪匹配 如:Stri ...
- 关于正则表达式的贪婪与非贪婪模式 - A JavaScript Fancier - IT博客网
以前看正则表达式,但没有注意到正则表达式的贪婪与非贪婪模式,今天在经典上看到了这么段代码: <script> try{ str=" abcdefg abcdefghijkl &q ...
- 94. 正则表达式贪婪与非贪婪模式的区别?
正则表达式,爬虫地时候用的比较多吧,暂时还没有接触过写爬虫地代码,先放下 写好正则表达式也是个不容易的事啊 1.什么是正则表达式的贪婪与非贪婪匹配 如:String str="abcaxc& ...
- java正则表达式非贪婪_正则表达式贪婪与非贪婪模式(示例代码)
之前做程序的时候看到过正则表达式的贪婪与非贪婪模式,今天用的时候就想不起来了,现在这里总结一下,以备自己以后用到注意. 1.什么是正则表达式的贪婪与非贪婪匹配 如:String str="a ...
- 正则表达式(四)——贪婪与非贪婪模式
高级用法在这里:正则表达式(三)--高级用法:search,findall,sub,split 8.贪婪与非贪婪模式 首先我们以简单的例子来说说什么是正则表达式的贪婪与非贪婪匹配? 比如假定匹配字符串 ...
- 正则表达式的贪婪和非贪婪模式
最近在写程序时,碰到一个场景,需要找到一个字符串中指定的一个片段,而不是所有片段,这就涉及到正则表达式中贪婪和非贪婪两种模式. 字面意思上,正则表达式一般趋向于最大长度匹配,就是贪婪模式.匹配到结果就 ...
最新文章
- All in one:如何搭建端到端可观测体系
- Spring-Cloud中的负载
- matlab数值过小为0,MATLAB数值计算——0
- 关于Virtual-Hosting的理解
- django16: csrf跨站请求伪造/CSRF相关装饰器
- element-ui Notification重叠问题,原因及解决办法
- PHP CURL 异步测试
- UIKit 框架之UIAlertController
- oracle查效能,Oracle 11g物理Active Data Guard实时查询(Real-time query)特性
- php 周末 培训,济南php周末培训班
- goolge hacks
- Redis-02-Redis数据类型与对不同类型数据的操作
- javaeye改名之后
- H5页面调起手机打电话功能
- itest听力答案2020_itest答案
- 用python做外贸
- 如何将bmp转化为jpg,bmp转jpg方法
- 超算计算机能不能玩游戏,拿神威太湖之光玩游戏会怎么样?
- Arduino Mega/Uno制作宏定义键盘(不需刷firmware!!)
- ubuntu20.04安装mysql8
热门文章
- aws lambda使用_使用AWS Lambda的CloudWatch事件通知
- kafka 发布订阅_在Kafka中发布订阅模型
- di容器_DI容器是代码污染者
- 在AWS第1部分中使用Terraform自动缩放组:基本步骤
- spring和spring_Spring交易可见性
- 了解自定义De / Serializer:JSON绑定概述系列
- 如何在Java中使用重复项查找整数数组中的K个缺失数字?
- Elasticsearch –使用模式替换过滤器和自定义分析器忽略查询中的特殊字符
- apache jmeter_Apache Server和JMeter调试
- java堆 数据结构 堆_Java中的紧凑堆外结构/组合