目录
1.正则表达式简介
2.Pattern和Matcher类简介
3.匹配规则
4.分组匹配
5.非贪婪匹配
6.搜索和替换
正则表达式简介
正则表达式的本质上是一个字符串,用来表示某种规则。正则表达式是一套标准,可以应用于任何语言,而不仅限于Java。
举一个简单的例子,“java"这个字符串,就可以当做一个正则表达式,但匹配的条件很苛刻,只有"java"能与之匹配成功,所以我们大部分时候还是会使用一些特殊字符去放宽匹配成功的条件。
再来看Java中一个简单的例子:我们如果想判断一个字符串,是不是"20xx”,那么对应的正则字符串可以写成"20\d\d" 。(\d就代表了0-9的任意一个数字)。
接着我们可以调用String对象的matches方法,来做判断,该方法要求传入一个正则字符串,返回值是布尔,如果对象匹配传入的正则字符串,则为真,否则为假。
String[] strs = {
"1997", // false
"2019", // true
"02019" // false
};
for (String s : strs) {
//因为在Java中,所以"\"要用"\\"表示
System.out.println(s.matches("20\\d\\d"));
}
使用正则表达式可以快速判断某个字符串是否符合我们想要的规则,Java内建的正则放在了java.util.regex包中
Pattern和Matcher类简介
正则表达式在java中想要实现更多的功能,就需要使用Pattern和Matcher。
Pattern和Matcher一般会配合使用。
Pattern p = Pattern.compile("20\\d\\d");//把正则编译成模具
Matcher m = p.matcher("22222");//让模具和字符串进行某种匹配操作
这里可以理解成Pattern会把正则表达式加工制作成一个模型,从而生成一个Pattern对象,然后这个对象可以把字符串与自己的模型进行匹配,但并不是返回一个布尔值,因为我们对模具和字符串有多种操作,而不是仅仅判断字符和正则是否匹配这么简单,所以它会返回一个Matcher对象。这个Matcher对象就可以做很多其它的操作(比如判断是否匹配,或某子串是否匹配,或字符串是否包含指定正则等等等等)。
从这里也可以看出,我们对于同一个正则表达式,只需要生成一次模具,即可多次重复使用。
Pattern除了compile()方法外,还有一些常用的:
Pattern p = Pattern.compile("\\d");
//String pattern()方法,返回被编译的正则式
p.pattern(); //输出结果为"\d",顺便提一下,Matcher对象也有pattern()方法,做的也是类似的事情
//String[] split(CharSequence input)
p.split("");// 分割字符串,String对象的split()方法,在底层也是调用的此方法
/*static boolean matches(String regex, CharSequence input)
静态函数,判断regex和input是否匹配,返回的布尔为匹配结果*/
Pattern.matches("\\d","2");// 返回true
Pattern.matches("\\d","t");// 返回true
下面来说一下Matcher对象可以把我们的字符串和正则模型做哪些匹配操作:
Pattern p = Pattern.compile("\\d");
Matcher m = p.matcher("7aa");
//boolean matches(),对整个字符串进行匹配,返回的布尔代表匹配是否成功
m.matches(); //在这里就是对"7"和"\\d"进行匹配,结果为true
//boolean lookingAt(),判断输入的字符是否是符合正则的开头
m.lookingAt();//在本例中,只要是以数字开头的字符串都可匹配成功
//比如"12aaa","111"就会为真,而"abc","a1234","a1cc"就会为假
//boolean find(),只要输入字符串中包含符合匹配规则的,就返回真
m.find();//本例中,也就是说只要有一个数字,就会返回true
/*boolean find(int startIndex),find()的重载,
传入的int型参数作用是,只看输入字符串中,下标start到最后的字串部分,
是否有匹配正则的部分,有则返回真*/
m.find(1);//本例中,输入的字符串如果不是"7"而是"7aa",就会失败,
// 因为它只会去看"7aa"下标从1往后的区间内,有没有符合规则的字符串
以上4个函数都是在查找某种条件下,输入的字符串是否和正则式相匹配,所有返回的都是布尔值。
下面再介绍2个函数,分别返回匹配到的字符串,在原字符串中的位置(也就是索引值)。
它们就是 int start()
和 int end()
函数。我们来看一下start()方法的源码:
/**
* Returns the start index of the previous match.
*
* @return The index of the first character matched
*
* @throws IllegalStateException
* If no match has yet been attempted,
* or if the previous match operation failed
*/
public int start() {
if (first < 0)
throw new IllegalStateException("No match available");
return first;
}
代码很简单,不过需要注意的是,以前很多函数在未匹配时会返回-1来表示失败,而这里是如果first属性小于0,则抛出异常,否则返回first。
我们可以看到注释中对抛异常的情况做出了解释,有2种情况:
- ①尚未尝试匹配:
直接调用start()函数,我们可以看到会抛出异常
这里就是因为,我们并没有对m进行相关的匹配操作(比如前面提到的4种布尔类型的函数),所以直接抛出了异常。通过下图,我们可以看到start的初始值为-1。
所以,我们想要使用start()
方法前,还需要进行某种匹配操作:
这里就可以看出,start()方法返回的是第一个匹配成功字符串的起始索引。 - ②之前的匹配操作失败
当然,如果匹配失败,也会抛出异常。比如:
关于end()
方法,使用起来和start()方法差不多,需要注意的就是,end()方法返回的索引值,并不是符合条件的字符串的最后一位的下标,而是最后以为下标的后一位。
还有一些用于分组的函数,我们到后面写完分组匹配后再举例
匹配规则
匹配规则这里就只放一个简单的归纳,不做举例了。
截图来自:https://blog.csdn.net/qq_16619993/article/details/97821374
推荐文章:https://gitchat.csdn.net/activity/5dadc09467d06a50783baaf0?utm_source=so
分组匹配
正则表达式中,中括号代码从里面选一个,大括号代表重复的次数,而小括号就代表了分组捕获。
比如一个正则字符串——"\d{3}\-\d{5}"
,它用来规定字符串必须是"xxx-xxxxx"的类型,并且x都得是数字。
如果我们想要获得前3为数组和后5为数字,就可以对正则加上小括号——"(\d{3})\-\(d{5})"
。这样我们通过Matcher对象的某些方法,就可以捕获到这两个字符串。比如:
改造完正则式后,关于如何获取到这2组字符串,我们则使用Matcher类中的 group()
方法。该方法和 start()
方法一样,同样要先进行匹配操作,并且匹配成功,然后才能对匹配上的字符串进行分组,否则会抛出异常。
调用 group()
方法会返回所匹配到的字符串:
Pattern p = Pattern.compile("(\\d{3})\\-(\\d{5})");
Matcher m = p.matcher("025-77777");
m.matches();
System.out.println(m.group()); //输出 025-77777
但这样我们依然没有拿到被分组的字符串,
这里我们就需要看group()
方法的重载版——String group(int group)
,该方法中传入的整型,就代表着小组的序号,0号代表了整个字符串,1号代表了第一个小组(本例中返回的也就是"025"),2就代表了第二个小组。像本例中,一共就2个小组,所以调用group(3)会抛出异常。
Pattern p = Pattern.compile("(\\d{3})\\-(\\d{5})");
Matcher m = p.matcher("025-77777");
m.matches();
System.out.println(m.group()); //输出 025-77777,其底层也是调用了group(0)
System.out.println(m.group(0)); //输出 025-77777
System.out.println(m.group(1)); //输出 025
System.out.println(m.group(2)); //输出 77777
关于嵌套的小括号,我们来看一下分组的顺序是什么。
现在有一个字符串——"( (x (u) ) ( y (z) ) )";
Pattern p = Pattern.compile("((x(u))(y(z)))");
Matcher m = p.matcher("xuyz");
m.matches();
System.out.println(m.group(0)); //输出 xuyz
System.out.println(m.group(1)); //输出 xuyz
System.out.println(m.group(2)); //输出 xu
System.out.println(m.group(3)); //输出 u
System.out.println(m.group(4)); //输出 yz
System.out.println(m.group(5)); //输出 z
只要被打了括号就会被算作一组,而第0组始终是整个被匹配的字符串,由于整体被加了括号,所以第一组就是整体的这个xyz。可以看出,分组是排序是类似"深度遍历"
的——“从外到里,从左到右”——某一个的括号先排到底,再排相邻括号。
非贪婪匹配
我们的正则式在匹配字符串时,会"贪婪地",也就是尽可能多得去匹配。举个例子,我们想要去把一串数字分组,分为"开头组合"和"末尾0的组合",如"1230000",我们就期望它分成"123"和"0000"这两组。我们如果按照以前的思路,会写出这样的正则表达式:
Pattern p = Pattern.compile("(\\d+)(0*)");
但很明显,因为自带的贪婪效果,"\d+“会直接匹配到"1230000”,而第二组只能匹配到空串。所以我们就要使用限制符——?
的方式来限制它的贪婪匹配,让这个正则式尽可能少得匹配到字符。比如:
Pattern p = Pattern.compile("(\\d+?)(0*)");
Matcher m = p.matcher("123000");
m.matches();
System.out.println(m.group(1)); //输出 123
System.out.println(m.group(2)); //输出 000
这样,我们就可以拿到想要的分组。
但需要注意的是,"?“在正则表达式中还表示匹配0或1个前方的正则式,那他们在一起时,我们就需要注意区别”?"所代表的含义。比如:
因为这里会非贪婪匹配,所以第一个括号就选择匹配到了0个字符
搜索和替换
首先讲搜索,这个其实就是把前面的方法做了一个应用,比如说我们有一句话——
"Royal Never Give Up"
,我们现在想要查找到句子中的"Never",就可以使用以下代码:
如果想找到一句中所有的“关键词”,还可以使用如下的方式:
控制台输出为👇
使用替换的时候,也可以使用正则表达式来做处理:
控制台输出为👇
除此之外,我们还可以给特定的字符来加一些额外的标签。