Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 724|回复: 0

捕获组,向前引用,零宽度断言,贪婪量词,惰性量词

  [复制链接]

该用户从未签到

发表于 2013-1-7 19:52:06 | 显示全部楼层 |阅读模式
捕获组
  正则表达式中的括号相当于一个分组,比如下面这个正则表达式,就把字符串分成了"_"分割的三个分组,
然后可以利用$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之间的数字越多,它比贪婪量词要回溯的就越少


回复

使用道具 举报

  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2013-1-7 19:52:23 | 显示全部楼层
    总结的非常好啊。。。
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-29 20:26 , Processed in 0.415425 second(s), 48 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表