✨✨hello,愿意点进来的小伙伴们,你们好呐!
🐻🐻系列专栏:【JavaSE】
🐲🐲本篇内容:详解正则表达式
🐯🐯作者简介:一名现大二的三非编程小白,日复一日,仍需努力。
- 基本语法:
- 语法入门:
- 字符匹配符
- 选择匹配符
- 限定符
- 定位符
- 分组符
- 正则表达式底层:
- 分组元字符:
- 反向引用:
- 常用类:
- String类中的使用
- 替换
- 判断
- 分割
正则表达式是一种程序员用来处理文本的利器
🍑🍑🍑正则表达式就是用某种模式去进行字符串匹配的一个公式,这个公式虽然看起来很起来,但是只要我们使用熟练后绝对是最靓的仔,用很古怪的公式,很高效的处理文本
接下来我来给大家介绍一下正则表达式的基本语法:
基本语法:
语法入门:
🛵🛵🛵下面我来写一个简单的正则表达式的模式匹配格式,带大家入门
public class Regexp_ {
public static void main(String[] args) {
//假设得到了以下文本
String content = "";
//我们写一个公式来进行模式匹配 -- 这个就是我们来进行模式匹配的正则表达式
String regStr = "([0-9]+)|([a-zA-Z]+)";
//先创建一个模式对象
//就是依照这个公式来进行匹配的模式
Pattern pattern = Pattern.compile(regStr);
//创建匹配器对象
Matcher matcher = pattern.matcher(content);
//循环匹配
//匹配器 按照模式 到content文本中去匹配,找到就返回true,否则就返回false
while (matcher.find()) {
//匹配内容 放到 group(0)
System.out.println("找到 : " + matcher.group(0));
}
}
}
正则表达式有很多元字符供你使用,如果想灵活的使用正则表达式的话,下面的元字符都必须了解清楚:
在我们使用正则表达式的去检索某些特殊字符的时候,需要用到转义符号 ,否则就无法检索,然后在Java的正则表达式中使用 \\ 来表示 \
元字符可以分为六种
下面我来给大家介绍一下这些元字符的使用方法
将采取介绍与代码实例来更清楚的理解
字符匹配符
public class test {
public static void main(String[] args) {
String content = "abc";
//String regStr = "[abc]";//寻找[abc]中其中之一
/*
找到 = a
找到 = b
找到 = c
*/
//String regStr = "[^ab]";//[^ab]找到非ab的元素
/*
找到 = c
*/
//String regStr = "[a-z]";// - 连接a与z 匹配a-z的任意一个字符
/*
找到 = a
找到 = b
找到 = c
*/
//String regStr = "a.c";//. -- 除了\n的其他的字符 匹配以a开头,c结尾,中间任意一个字符
/*
找到 = abc
*/
//String regStr = "\\d";//匹配单个数字字符 ,
//String regStr = "\\D";//匹配非数字字符 == [^0-9]
//String regStr = "\\w";//匹配单个[0-9a-zA-Z]的字符
//String regStr = "\\W";//匹配单个[^0-9a-zA-Z]的字符
String regStr = "";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while(matcher.find()) {
System.out.println("找到 = " + matcher.group(0));
}
}
}
选择匹配符
public class test2 {
public static void main(String[] args) {
String content = "abc";
String regStr = "[a|b]";//匹配a或者b的其中一个
/*
找到 = a
找到 = b
*/
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while(matcher.find()) {
System.out.println("找到 = " + matcher.group(0));
}
}
}
限定符
public class test3 {
public static void main(String[] args) {
String content = "abc";
//String regStr = "(ab)*";//匹配是否有(ab)字符串出现次数至少0次
/*
找到 = ab
找到 =
找到 =
//后面两个都是找到(ab)出现0次
*/
//String regStr = "(ab)+";//匹配(ab)出现至少一次
/*
找到 = ab
*/
//String regStr = "(a?ab)";//开头是否有a都会匹配到,后面接ab
/*
找到 = ab
*/
//String regStr = "[abcde]{2}";//[abcde]中任意两个字符,从content字符串头开始匹配
/*
找到 = ab
*/
//String regStr = "[abcde]{2,}";//{2,} 最少有两个字符匹配
/*
找到 = abc
//因为正则表达式默认情况下是贪婪匹配的,只会往多了去匹配
*/
String regStr = "[abcde]{1,2}";//字符串中最少有一个字符匹配,最多有两个字符匹配
/*
//也是遵循贪婪匹配
找到 = ab
找到 = c
*/
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 = " + matcher.group(0));
}
}
}
定位符
public class test4 {
public static void main(String[] args) {
String content = "00sds111";
//String regStr = "^[0-9]+[a-z]*";//匹配至少以一个数字字符开头,后面接任意个小写字母字符
/*
找到 = 00sds
*/
//String regStr = "^[0-9]+[a-z]*[0-9]+$";//匹配以最少一个数字字符,最少一个数字字符结尾,中间有最少0个小写字母字符
/*
找到 = 00sds
*/
//String regStr = "[a-z]*111\\b";//匹配以111为右边界的第一个字符(空格也算边界),左边接最少0个小写字母字符
/*
找到 = sds111
*/
String regStr = "00\\B[a-z]*";//匹配以00为左边界的第一个字符,右边最少接0个1小写字母
/*
到 = 00sds
*/
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 = " + matcher.group(0));
}
}
}
分组符
在解释分组匹配符之前我先来介绍一下正则表达式的底层实现
正则表达式底层:
我们来通过源码来了解正则表达式的底层是什么样子的
public class RegTheory2 {
public static void main(String[] args) {
String content = "123abc789";
String regStr = "\\d{3}";
//创建模式对象
//说明寻找规则
Pattern pattern = Pattern.compile(regStr);
//匹配器
//创建一个匹配器,按照 正则规则去匹配content这个字符串
Matcher matcher = pattern.matcher(content);
//matcher.find() 去循环匹配是否有该规则的字符串,返回true或者false
while (matcher.find()) {
//matcher.group(0)
System.out.println("找到 = " + matcher.group(0));
}
}
}
我们通过Pattern类中的静态方法compile来创建一个正则表达式规则的模式匹配
然后通过这个模式去调用matcher方法来返回一个匹配器对象.
我们会发现打印的group()方法,中其实是使用字符串截取,其中涉及到group[]数组
然后group[]数组是Matcher中的一个属性,是为什么会截取中的是取group[]数组中的值呢,然后还有group * 2 / group * 2 + 1 这个算法
接下来我们来对代码调试找出原因
🍨🍨🍨🍕调试代码
代码继续往下走
我们在matcher对象的groups数组中发现下标0与1都被赋值,分别是0或者3,那么是怎么回事呢?
其实就是按照当前的匹配模式在字符串中1匹配到的字符串的 [开始下标与结束下标+1],
然后在group方法中,传入的group值为0,所以最后截取达到就是groups数组中下标为0或者1的值,对应的就是原本要匹配的字符串的下标
在第一次匹配查找底层就是如此,那么循环第二次第三次会是怎么样的呢?
🌮🌮🌮
我们会发现在matcher对象中还有一个last与oldlast,且值是我们刚刚匹配到的字符串的结尾下标,其实这个值就是让我们使用于下一个匹配的开始下标! ! !
我们继续走源码,看看究竟会发生什么?
继续走源码,依旧找到的是匹配的字符串的开始下标和结束下标+1,再一次证明了我们刚刚的结论是正确的
那这个时候我们就会有疑问了,这个数组为什么只有前两个下标存储了内容,那其他的内存是来干什么的呢?其实就和我们上面还没有介绍的分组操作符有关了
接下来我们再来看看是怎么回事
分组元字符:
public class RegTheory2 {
public static void main(String[] args) {
String content = "123456789";
String regStr = "(\\d{3})(\\d{3})(\\d{3})";
//创建模式对象
//说明寻找规则
Pattern pattern = Pattern.compile(regStr);
//匹配器
//创建一个匹配器,按照 正则规则去匹配content这个字符串
Matcher matcher = pattern.matcher(content);
//matcher.find() 去循环匹配是否有该规则的字符串,返回true或者false
while (matcher.find()) {
//matcher.group(0)
System.out.println("找到 = " + matcher.group(0));
}
}
}
继续调试我们发现groups[]数组中会有更多的空间使用到了
分组的第一组下标的就是在groups[]数组中下标为2,3的数,以此类推
那么分组后,我们想取第一组的值,就调用Matcher类中的group()方法,传入1
传入1计算后,就会发现与groups[]中存储第一组字符串的下标是对应的
🛫🛫🛫总结:对应正则表达式匹配来说,是通过原本的字符串下标截取来体现出我们匹配的结果的(我们称为结果下标),那么结果下标的存储都是在Matcher类中的groups[]数组中的,groups[]数组中的前两个空间存储的就是匹配到的整个字符串的开始结束下标,那么后面的每两个空间存储的就是在匹配规则中的每一个分组的字符串的开始结束下标.存储下标完,通过group()方法返回,该方法返回的就是字符串截取,然后通过方法的算法得出,group(0)返回的就是整个匹配的字符串,然后group(1)返回的就是第一个分组匹配到的字符串,以此类推
⛴⛴⛴🚓🚓🚓然后我们可以通过给分组命名
public class RegTheory2 {
public static void main(String[] args) {
String content = "123456789";
String regStr = "(?<a>\\d{3})(?<b>\\d{3})(?<c>\\d{3})";
//创建模式对象
//说明寻找规则
Pattern pattern = Pattern.compile(regStr);
//匹配器
//创建一个匹配器,按照 正则规则去匹配content这个字符串
Matcher matcher = pattern.matcher(content);
//matcher.find() 去循环匹配是否有该规则的字符串,返回true或者false
while (matcher.find()) {
//matcher.group(0)
System.out.println("找到 = " + matcher.group(0));
System.out.println("找到 = " + matcher.group("a"));
System.out.println("找到 = " + matcher.group("b"));
System.out.println("找到 = " + matcher.group("c"));
}
}
}
这些则是不捕获的匹配表达式,这些表达式使用后,在group[]数组中是不在捕获到分组的下标了
反向引用:
我们对应分组的圆括号内被捕获后,这个分组还可以被继续引用,称为反向引用,这个引用可以在正则表达式内部,也可以在外部,内部反向引用–\分组号,外部反向引用$分组号
内部引用
public class RegTheory2 {
public static void main(String[] args) {
String content = "1122";
String regStr = "(\\d)\\1";
//创建模式对象
//说明寻找规则
Pattern pattern = Pattern.compile(regStr);
//匹配器
//创建一个匹配器,按照 正则规则去匹配content这个字符串
Matcher matcher = pattern.matcher(content);
//matcher.find() 去循环匹配是否有该规则的字符串,返回true或者false
while (matcher.find()) {
//matcher.group(0)
System.out.println("找到 = " + matcher.group(0));
}
}
}
匹配两个一样的字符就可以使用到反向引用
"结巴程序"化为不结巴就可以使用反向引用中的外部引用来化解:
public class RegTheory2 {
public static void main(String[] args) {
String content = "我....我要....学学学学....编程 java!";
String regStr = "\\.";
//创建模式对象
//说明寻找规则
Pattern pattern = Pattern.compile(regStr);
//匹配器
//创建一个匹配器,按照 正则规则去匹配content这个字符串
Matcher matcher = pattern.matcher(content);
//Matcher 中的replaceAll方法是一个替换所有方法,将字符串原本根据正则表达式匹配到的值替换为传入的字符串参数;
//1.去掉...
content = matcher.replaceAll("");
//2.去掉重复的字
//使用(.)\\1+ 匹配到重复的字
//然后使用反向引用$1 来替换
regStr = "(.)\\1+";
pattern = Pattern.compile(regStr);
matcher = pattern.matcher(content);
content = matcher.replaceAll("$1");
//content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");
System.out.println(content);
}
}
常用类:
在java.util.regex中主要包括 Pattern 类, Matcher 类 和 PatternSyntaxException 异常类
Pattern类
Pattern对象是一个正则表达式的对象.Pattern类没有公共的构造方法,所以无法直接创建,只能通过类中的静态方法来返回一个Pattern对象,该方法可以传入一个正则表达式来作为参数
String regStr = "(?<a>\\d{3})(?<b>\\d{3})(?<c>\\d{3})";
//创建模式对象
//说明寻找规则
Pattern pattern = Pattern.compile(regStr);
Matcher类
Metcher 对象是对要来进行匹配的字符串的解析和匹配的引擎,与Pattern类一样,Matcher类也没有公共的构造方法,想获取该类对象,只能通过Pattern对象的matcher方法来获取
//匹配器
//创建一个匹配器,按照 正则规则去匹配content这个字符串
Matcher matcher = pattern.matcher(content);
PatternSyntaxException 异常类
该类就是来检查正则表达式公式中的语法错误,是一个非强制异常类.
String类中的使用
正则表达式作为处理字符串文本的利器,在Java中的String理所应当要用正则表达式对应的方法啦
替换
将原本的JDK1.3/1.4 替换为JDK
public class test6 {
public static void main(String[] args) {
String content = "2000年 5月,JDK1.3、JDK1.4和 J2SE1.3相继发布,几周后其"
+"获得了 Apple公司 Mac OS X的工业标准的支持。2001年 9月 24日,J2EE1.3发" +"布。" +"2002年 2月 26日,J2SE1.4发布。自此 Java的计算能力有了大幅提升";
content = content.replaceAll("JDK1\\.3|JDK1\\.4", "JDK");
System.out.println(content);
}
}
这样子节省了创建模式,创建匹配器对象的操作,很方便的使用到正则表达式
判断
public class test6 {
public static void main(String[] args) {
String content = "13888889999";
if (content.matches("1(38|39)\\d{8}")) {
System.out.println("验证成功");
} else {
System.out.println("验证失败");
}
System.out.println(content);
}
}
我们往String类的matches方法深入解剖下去
最终还是调用Pattern的matches()方法
分割
往split方法中也可以传入正则表达式来继续字符串的分割匹配
public class test6 {
public static void main(String[] args) {
//要求按照 #或者 -或者 ~或者数字来分割
String content = "hello#abc-jack12smith~北京";
String[] split = content.split("#|-|~|\\d+");
for (String s :
split) {
System.out.println(s);
}
}
}