正则表达式
1. 正则表达式
1.1. 概述
在日常对一些软件的使用,经常会遇到:用户输入手机号,对手机号的格式验证;需要从一段字符串中截取满足条件的字串,此时需要做匹配查找;在一些特定场景下需要将输入的内容中包含的敏感词替换成其他等需求;因此对字符串的一些特殊处理包括:验证,查找,替换等。针对这些需求目前在开发领域有意向技术可以满足需求:正则表达式(Regex)。
正则表达式最早源自于perl
语言,后来广泛的被其他(c,c++,java,c#,javascript…)语言接受;java中对于正则表达式提供了几个常用类:
java.lang.String
java.util.regex.Pattern
java.util.regex.Matcher
1.2. 元字符匹配
元字符匹配即对单个的字符内容匹配,正则表达式中包含以下常见的符号实现匹配规则
元字符 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字 |
\s | 匹配任意的空白符(空格,制表符,换行) |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
\W | 匹配非单词字符(非字母,非数字) |
\S | 匹配非空白字符 |
\D | 匹配非数字 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
[0-9] | 匹配数字 |
[a-zA-Z] | 匹配字母 |
[^0-9] | 匹配非数字 |
1.3. 长度匹配
长度匹配指的是需要匹配的字符串长度
语法 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
{0,n} | 重复0次或者n次 |
1.4. Pattern&Matcher
通过对String
类中matches
方法了解,可以看出来该方法的实现是基于java.util.regex.Pattern
和java.util.regex.Matcher
类来合作完成。
Pattern
是正则表达式的编译表达形式,在java中正则表达式需要产生效果,一般是需要先经过编译环节,才能具体使用:
Pattern p = Pattern.compile(正则表达式);
Matcher
:使用Pattern
完成编译之后,将会产生一个匹配模式对象,接下来就可以通过对固定的字符串匹配获取匹配器:
Matcher m = p.matcher(目标字符串);
匹配器包含三个主要功能:
1.5. 分组
在正则表达式编写时可以对表达式内部的子串进行分组匹配,例如以下表达式:
(a(b(c)))
包含如下分组:
- 第0组
(a(b(c)))
- 第1组
a(b(c))
- 第2组
(b(c))
- 第3组
(c)
String regex = "(\\w(\\w(\\w)))";
String str = "abc";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
m.find();
System.out.println("第0组:"+m.group(0)); // abc
System.out.println("第1组:"+m.group(1)); // abc
System.out.println("第2组:"+m.group(2)); // bc
System.out.println("第3组:"+m.group(3)); // c
1.6 三种匹配模式
1.6.1. 贪婪模式(greedy)
贪婪模式即尽可能多的匹配,使用+
符号,例如:
String s = "abbcccccsfsdccc";
String regex = "a\\w+c";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s);
while(m.find()){
System.out.println(m.group());
}
运行结果:
abbcccccsfsdccc
1.6.2. 勉强模式(reluctant)
勉强模式也称之非贪婪模式,使用是最短匹配,符号?
,例如:
String s = "abbcccccsfsdccc";
String regex = "a\\w+?c";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s);
while(m.find()){
System.out.println(m.group());
}
运行结果:
abbc
1.6.3. 独占模式(possessive)
独占模式跟贪婪模式的区别在于,不会回溯,即一直往后搜索会将后续的所有字符串进行匹配
String s = "aabbbccddcaabbbdd";
String regex = "aa.*+dd";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s);
m.find();
System.out.println(m.group());
结果:
Exception in thread "main" java.lang.IllegalStateException: No match found at java.util.regex.Matcher.group(Unknown Source) at java.util.regex.Matcher.group(Unknown Source) at com.softeem.lesson20.regex.ReluctantDemo.main(ReluctantDemo.java:27)
1.7 正则表达式补充(后向引用)
正则表达式提供的后向允许使用()
对表达式分组之后,使用$编号
方式引用表达式所匹配的内容,例如:手机号脱敏操作
- 输入手机号:
13854678954
- 输出手机号:
138****8954
使用后向引用方式,只需一行代码即可:
String phone = "13854678954".replaceAll("(1\\d{2})\\d{4}(\\d{4})","$1****$2");
后向引用可以结局很多数据格式转换问题:
//有一个日期 2021-7-22 显示为 2021年7月22日
System.out.println("2021-7-22".replaceAll("(\\d{4})-(\\d{1,2})-(\\d{1,2})","$1年$2月$3日"));
//以上问题传统的解决方法
String sDate = "2021-07-22";
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
Date date = fmt.parse(sDate);
fmt = new SimpleDateFormat("yyyy年MM月dd日");
System.out.println(fmt.format(date));
//有一个日期 2021-7-22 09:06:33 显示为 2021年7月22日 09时06分33秒
regex = "(\\d{4})-(\\d{1,2})-(\\d{1,2})\\s+?(\\d{1,2}):(\\d{1,2}):(\\d{1,2})";
System.out.println("2021-7-22 09:06:33".replaceFirst(regex,"$1年$2月$3日 $4时$5分$6秒"));