常用常新:正则表达式在Java中的高阶使用
基本使用在上篇:juejin.cn/post/715356…
一、分组匹配
分组匹配可以用一条正则来同时匹配并提取字符串各组的值,比如一个固定电话号可以同时提取区号和座机号。
具体使用分组匹配符号
()来分组,各组从左到右按顺序排列,如group(1)表示第一组,group(2)表示第二组,值得注意的是group()与group(0)都表示数据本身。
1.1 提取固定电话的座机号和区号
String s="021-7729159";
Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$");
Matcher matcher = pattern.matcher(s);
if(matcher.matches()){
System.out.println(matcher.groupCount());
System.out.println(matcher.group());// 调用的是group(0) 021-7729159
System.out.println(matcher.group(0));// 021-7729159
System.out.println(matcher.group(1));//021
System.out.println(matcher.group(2));// 021
}
1.2 提取完整时间年月日时分秒
String s = "2022-12-13 19:31:59";
Pattern pattern = Pattern.compile("^(\\d{4})-([01]\\d)-([0-3]\\d) ([0-2]\\d):([0-5]\\d):([0-5]\\d)$");
Matcher matcher = pattern.matcher(s);
if (matcher.matches()) {
System.out.println(matcher.groupCount());
for (int i = 1; i <= matcher.groupCount(); i++) {
System.out.println(matcher.group(i));
}
}
// 结果: 6 2022 12 13 19 31 59
二、贪婪匹配
贪婪匹配情况只有在多分组匹配前提下才会出现,在多个分组都能匹配到同一段字符串时,前一个分组匹配的字符多了,后一个分组匹配的字符就少,甚至没有,可能影响最终分组结果。
-
Java默认是贪婪匹配
-
可以在分组正则后面再加
?改为非贪婪匹配,会尽可能的少匹配字符
例子:想要将字符串1230000分为两组,前面123为一组,后面0000为一组。正则如果写成^(\\d+)(0*)$,第一组正则贪婪匹配,一直匹配到最后一个字符串了,为1230000,第二组无字符可匹配,为""。 解决这问题,需要把第一组正则改为非贪婪的,可以在第一组正则最后加?字符改为非贪婪匹配。
// 贪婪匹配
String s = "1230000";
Pattern pattern = Pattern.compile("^(\\d+)(0*)$");
Matcher matcher = pattern.matcher(s);
if (matcher.matches()) {
System.out.println(matcher.groupCount());
for (int i = 1; i <= matcher.groupCount(); i++) {
System.out.println(matcher.group(i));
}
}
// 结果:2 "1230000" ""
// 非贪婪匹配
String s = "1230000";
Pattern pattern = Pattern.compile("^(\\d+?)(0*)$");
Matcher matcher = pattern.matcher(s);
if (matcher.matches()) {
System.out.println(matcher.groupCount());
for (int i = 1; i <= matcher.groupCount(); i++) {
System.out.println(matcher.group(i));
}
}
// 结果:2 "123" "0000"
三 、分割、搜索
3.1 字符串分割
字符串可以用单条件或者多条件分割为数组,多条件兼容性更强,在用户输入的情况下比较好用。例如用户数入的数据中英文逗号混用等。
// 1.单条件分割,根据空格分割,空格可多个
String s1 = "a b cd e";
System.out.println(Arrays.toString(s1.split("\\s+")));
// 结果:[a, b, cd, e]
// 多条件分割 ,根据空格、逗号、顿号之一分割,可多个
String s2="a, b ;; c";
System.out.println(Arrays.toString(s2.split("[\\s,;]+")));
// 结果:[a, b, c]
3.2 搜索
查找出字匹配符串中符合正则的所有字符或字符串,可以保存起来显示或进行统计。
// 搜索 ax 类型的字符串
String s = "abc def asae";
Pattern p = Pattern.compile("a\\w");
Matcher m = p.matcher(s);
while (m.find()) {
String sub = s.substring(m.start(), m.end());
System.out.println(sub);
}
// 结果: ab as ae
四、 字符模板替换
类似简单的模板引擎,把字符串模板中占位符类似${name}替换为动态真实的name数据。
在发送短信、邮件时,消息模板不固定、经常变动。先定义相关参数,发送时,可以动态替换模板中的占位符。上线后编辑模板也不需要再发版了。
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("name", "大王");
map.put("docu", "报销单");
String s = "你好${name},你的${docu}单据已经审批通过了!";
System.out.println(templateReplace(map, s));
}
public static String templateReplace(Map<String, String> map, String s) {
Pattern p = Pattern.compile("\\$\\{\\w+}");
Matcher m = p.matcher(s);
StringBuffer sbf = new StringBuffer();
while (m.find()) {
// 类似从占位符中${name}找到name
String key = s.substring(m.start() + 2, m.end() - 1);
// 替换
m.appendReplacement(sbf, map.getOrDefault(key,""));
}
m.appendTail(sbf);
return sbf.toString();
}