Pattern
[TOC]
Pattern
贪婪与非贪婪匹配
定义
贪婪匹配是尽可能匹配多的字符
非贪婪匹配就是尽叮能匹配少的字符
贪婪匹配的写法是.*,非贪婪匹配的写法是.*?,多了一个?
举例
例如:
贪婪匹配
/**
* 贪婪匹配
*
* 运行结果:
* group:A12
* group:3
* replaceAll:
*/
@Test
public void test2() {
Pattern pattern = Pattern.compile("WORD_(.*)(\\d+)_321");
Matcher matcher = pattern.matcher("WORD_A123_321");
while (matcher.find()) {
System.out.println("group:" + matcher.group(1));
System.out.println("group:" + matcher.group(2));
}
System.out.println("replaceAll:" + matcher.replaceAll(""));
}非贪婪匹配
但是这里需要注意,如果匹配的结果在字符结尾,.*?就有可能匹配不到任何结果了,因为他会尽可能匹配少的字符(换句话说,都到末尾了,我尽可能少,那就是不匹配),例:
从Compile到Matcher
下面是使用正则的一种经典写法:
从这个写法中,我们不难看出其中两大重要方法:compile()、matcher()。
接下来我们将从这两个方法入手,深入看下,源码中如何玩转的。
Compile
阿里巴巴开发规范中,有这么一条:
【强制】在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
说明:不要在方法体内定义: Pattern pattern = Pattern.compile(“ 规则 ”);
大概我们能猜到compile()会做一些耗时的事情,那么,带着我们的猜想去看看compile()干了啥。
这边是主动调用了私有的一个构造器,我们接着看:
仔细看下构造器,其他的东西都不怎么引人注目,也不会很耗时,只有compile()这个方法(切记此compile(),不要和Pattern.compile()混淆哈),可能有点神秘,我们继续往下看:
matchRoot = expr(lastAccept);,这一行,看起来有一点说法:
Node node = sequence(end);,其他的,看起来都是在组装Node,只有这个是在获取Node,我们接着看:
看到这里,好像有点清晰了,我们在来梳理下,Node node = sequence(end);负责根据不同的字符,产生不同的Node,然后拼接到Node中去,而matchRoot = expr(lastAccept);看起来就更清晰了,根据输入的字符串通过Node node = sequence(end);生成不同的Node,然后在连接在一起,所以我们可以很自信的讲出,原来我们写的正则表达式,最后会在compile()方法中,被matchRoot = expr(lastAccept);解析成一种单链表结构。当然compile()方法还有一些其他判断处理,这些对于常规的表达式来说不是重点,就现在而言,你可以认为她是在处理一些特殊情况,后续我们在详细过一遍源码的细节。
防止大家还有点模糊,说这一大堆文字,谁看的懂呀,不画个图意思意思?OK,图来:
这样够清晰了吗,嘻嘻,好叻这样的一个流程,我们应该十分清晰了,下面将从其他细节部分再次深入了解。
Matcher
好了,看完Compile,我们再来看看经典写法:
很明显,我们每次判断是否匹配上时,都通过matcher.find()进行查找。于是,我们大胆猜测,匹配的算法就在find方法中,让我们一起打开她的神秘面纱:
同样的,我们抛去不太重要的代码行,直接来看核心search(nextSearchIndex):
可以非常清晰的看到 boolean result = parentPattern.root.match(this, from, text);这一行,在结合我们上面Compile的探索,发现此行的目的为:从根节点Root开始match。
有心的小伙伴,肯定有些纳闷,单链表的访问至少也有个指针负责遍历吧,这里咋就看起来遍历一次,就没了呢,别急,继续往下看。
之前我们看Compile也讲过,Pattern类中有着不同的Node实现,她们之前有一个叫做Start的类,上面的parentPattern.root便是这个类的实例,下面我们来看看:
看到这边,是不是有点熟悉的味道了,有循环,有下一个节点,但是又有点不同,还是没有找到我们最熟悉的指针,仔细观察方法的返回值,是个boolean类型,那看起来是每个实现类都完成自己的匹配,并返回匹配结果,然后不属于自己匹配的部分,交由Next去匹配,我们暂且称作链表匹配,带着我们猜测,再去看看其他Node的实现:
等等。如果你有耐心,大可以看完所有的Node实现,最后一定会发现,每个Node完成自己的任务后,调用Next的Match方法。
最后,我们还是画上一张图,来说明下核心调用过程(涉及到的Node,仅为了演示,不分先后顺序):
Last updated