正则表达式

本文介绍了正则表达式中的环视概念,包括前瞻和后顾环视,以及Matcher的matches()和find()方法的区别。通过实例详细解释了环视如何定位文本中的特定位置,以及它们在匹配过程中的作用。同时,文章还讨论了逆向环视时模式长度的限制和(?:pattern)的非获取匹配功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习网站

https://www.runoob.com/regexp/regexp-syntax.html

Matcher成员方法matches()和find()区别

1、matches()

功能:尝试根据模式匹配整个区域

注意:匹配的是整个区域

2、find()

功能:尝试查找与该模式匹配的输入序列的下一个子序列。

注意:此方法从该匹配器区域的开始处开始,或者,如果该方法的前一次调用成功,匹配器也成功了,并且没有被重置,在上一次匹配成功后不能被匹配的第一个字符。

3、一种情况:单独使用find()可以查到,但在matches()之后使用find(),不能查到。

//创建指定匹配规则的模型
Pattern pattern = Pattern.compile("ab([1-8]*)f");
//创建匹配器
Matcher matcher = pattern.matcher("ab5625836f");
 
if(matcher.matches()) {
    System.out.println(matcher.start() + " " + matcher.end() + " " + matcher.group());
} else {
    System.out.println("没有匹配到!");
}
 
if(matcher.find()){
    System.out.println(matcher.start() + " " + matcher.end() + " " + matcher.group());
}

find()方法源代码:

public boolean find() {
    int nextSearchIndex = last;
    if (nextSearchIndex == first)
        nextSearchIndex++;
 
    // If next search starts before region, start it at region
    if (nextSearchIndex < from)
        nextSearchIndex = from;
 
    // If next search starts beyond region then it fails
    if (nextSearchIndex > to) {
        for (int i = 0; i < groups.length; i++)
            groups[i] = -1;
        return false;
    }
    return search(nextSearchIndex);
}

注意第一句代码::int nextSearchIndex = last;

在之前matches()执行后,last=10;所以nextSearchIndex从索引10开始查询,自然就查不到了。

环视

(?=pattern)与(?!pattern)属于一类,在正则表达式中叫环视。 “环视”这个词从字面理解就是确定“周围环境”。环视一共有四种:(?=pattern)、(?!pattern)、(?<=pattern)、(?<!pattern)。前两种是顺序环视,也叫做前瞻;后两种叫逆序环视,也叫后顾。每种又有“=”和“!”之分,前者是肯定(满足pattern则匹配成功),后者是否定(不满足pattern则匹配成功)。通过例子会更好理解,考虑下面字符串:

目标字符串:ABCDEFG ACDIU

现在我们用简单的字符串匹配的正则表达式:

正则表达式:CD

显然会得到两个结果,一个是“ABCDEFG”中的CD,一个是“ACDIU”中的CD;现在我不想匹配到后面的那个CD,因为我只想匹配紧接着"EFG"的那个CD,环视就可以派上用场(当然,这种简单的例子,其他方式也完全可以解决),我们可以这么做:

正则表达式1:CD(?=EFG)

此时,我们只会得到一个结果,就是“ABCDEFG”中的CD!这是为什么呢?这就是(?=pattern)起的作用。我们模拟下正则表达式的处理过程(当然,我暂时没有学习正则表达式引擎的原理,不确定是否就是完全正确的过程,但是有助于理解):引擎从左向右扫描,扫描到“ABCDEFG”中的CD时,第一次扫描到“CD”字符,然后执行环视,(?=)表示顺序环视,也就是向字符串右边看,观察后面的字符是否是“EFG”,如果是,则匹配成功;如果不是,则丢弃这个匹配。因此出现了上述结果。
下面看逆序环视:

正则表达式2:(?<=A)CD

此时,向左看,看是否是“A”字符,因此,正则表达式2只能匹配到“ACDIU”中的CD。

环视不消耗字符

还是用示例来理解:

正则表达式3:CD(?=EF)G

正则表达式3运行后能匹配到东西吗?答案是不能!理解环视不消耗字符很重要:引擎在扫描到“CD”后,执行环视,观察右边是否有“EF”字符,结果是观察到了,那么继续匹配后面的,但此时引擎仍然从“D”这个字符的位置往后扫描。因为,扫描到“CD”后,引擎在环视的过程中并没有消耗后面的“EF”字符,引擎仍从上次扫描的位置接着扫描,显然“D”后面的字符不是“G”,因此匹配失败。
如果改成下面的表达式,就能匹配到了

正则表达式3:CD(?=EF)E

本质:环视不匹配任何字符,只匹配文本中的特定位置

前面说了那么多,其实是为了好理解。但说法可能并不规范,真正的环视的理解是这样的:环视不匹配任何字符,只匹配文本中的特定位置。环视是为了定位特定的位置,如果目标字符串中这个位置(位置可能是多个)的左右字符能(否定环视则是不能)匹配上正则表达式中环视表达式左右的字符串,那么匹配成功,这点非常重要。
不太好解释,直接上示例吧:

**正则表达式4:C(?=EFG)D //失败,因为(?=EFG)定位结果是D字符后面、E字符前面 **

**正则表达式5:C(?=DEFG)D //成功 **

**正则表达式6:CD(?<=AB) //失败,(?<=AB) 定位结果是B字符后面、C字符前面 **

正则表达式7:CD(?<=ABCD) //成功 正则表达式7:(?<=AB)CD //成功

终极示例是这个:
考虑这样一个应用情景:对于一个大的数值,比如35689412,西方国家习惯三位一个量级,中间以逗号隔开,方便一眼看出大小,也即是35,689,412。那么现在有一堆数,请用正则表达式完成替换,替换成诸如35,689,412的格式。题目分析:也就是从右往左数,每隔三个数,如果前面还有数的话就在前面加个逗号。也就是找到这些特定的位置,在该位置处加上“,”。

正则表达式8:(?<=\d)(?=(\d{3})+$)

正则表达式8全由环视结构组成,没有匹配任何字符(如果用group()函数看的话,结果是多个空字符串),但是却匹配了一堆位置。再用:replaceAll(",”),即可完成问题要求。

 @Test
 public void replaceAllTest(){
	String number = "82359123650916359816359";
	String regex = "(?<=\\d)(?=(\\d{3})+$)";
    String formatNumber = number.replaceAll(regex,",");
	System.out.println(formatNumber);//82,359,123,650,916,359,816,359
 }
逆向环视时,pattern长度必须是固定的

有一点需要注意:逆向环视时,pattern长度必须是固定的,也就是说pattern中不能出现诸如*.等这类字符使得pattern匹配的长度不固定;但是前向环视没有这个要求。

(?:pattern)

(?:pattern)则跟环视根本就不是一类东西,只是长得比较像,他们的最大区别就是:(?:pattern)是匹配文字,也就是会消耗字符。比如将正则表达式3做如下修改:

正则表达式3_1:CD(?:EF)G

改成(?:EF)后,匹配成功。(?:pattern)是和(pattern)相对应的,他们的区别在于:(pattern)是获取匹配,pattern内容会出现在匹配结果的集合中;(?:pattern)是非获取匹配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值