常用常新:正则表达式在Java中的高阶使用

366 阅读1分钟

常用常新:正则表达式在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();
    }