• 作者:老汪软件技巧
  • 发表时间:2024-09-06 07:01
  • 浏览量:

本文不讲解正则表达式相关的知识内容,如对这块知识不太了解的同学,请先移步至正则表达式 – 教程 | 菜鸟教程学习相关知识;本文主要讲解在Java中如何利用正则表达式进行模式匹配相关知识内容。

在Java中使用原生JDK实现正则表达式的匹配,不可避免的我们需要使用到java.util.regex包下的两个类Pattern和Matcher

初识模式匹配

我们的一般用法,就是先定义一个正则串和匹配串,然后看匹配串中是否有满足正则串条件的字符序列;如下:

public class Demo1 {
    public static void main(String[] args) {
        //[a-z]表示a~z之间的任何一个字符, {3}表示3个字符, 意思是匹配一个长度为3, 并且每个字符属于a~z的字符串
        Pattern p = Pattern.compile("[a-z]{3}");
        Matcher m = p.matcher("abc");
        System.out.println(m.matches());
    }
}
//输出结果
true

完全匹配和部分匹配

通过上面的Demo1样例中,我们以经初步了解了Java对正则表达式匹配的简单使用;我们再来看下面这个Demo2样例

public class Demo2 {
    public static void main(String[] args) {
        //[a-z]表示a~z之间的任何一个字符, {3}表示3个字符, 意思是匹配一个长度为3, 并且每个字符属于a~z的字符串
        Pattern p = Pattern.compile("[a-z]{3}"); // 正则串 - "[a-z]{3}"
        Matcher m = p.matcher("abcd");  // 模式串 - "abcd"
        System.out.println(m.matches());
    }
}

大家可以猜测这个结果会输出什么呢?此处停留3秒~~~

大家可以复制这段代码运行一遍,会惊奇的发现,现在输出的结果变为了false而不再是原来的true了;

为什么出现上面这个情况呢?我们仅仅是在匹配串里面多加了一个d字符,为什么就会导致匹配不上了呢?

要回答上面的问题,就需要了解完全匹配和部分匹配的区别;

使用Matcher.matches()方法判断匹配串是否匹配到正则串是一种完全匹配的判断,即需要完全匹配的情况下才会返回true;而如果我们想如果部分匹配到了就返回true,我们就需要使用Matcher.find()来判断是否部分匹配到结果

public class Demo3 {
    public static void main(String[] args) {
        //[a-z]表示a~z之间的任何一个字符, {3}表示3个字符, 意思是匹配一个长度为3, 并且每个字符属于a~z的字符串
        Pattern p = Pattern.compile("[a-z]{3}"); // 正则串 - "[a-z]{3}"
        Matcher m = p.matcher("abcd");  // 模式串 - "abcd"
        System.out.println(m.find());
    }
}

执行Demo3样例会发现现在打印出的结果过即是true

起始/终止标识符

public class Demo4 {
    public static void main(String[] args) {
        //[a-z]表示a~z之间的任何一个字符, {3}表示3个字符, 意思是匹配一个长度为3, 并且每个字符属于a~z的字符串
        Pattern p = Pattern.compile("^[a-z]{3}$"); // 正则串 - "^[a-z]{3}$"
        Matcher m = p.matcher("abcd");  // 模式串 - "abcd"
        System.out.println(m.find());
    }
}

执行Demo4样例会发现现在打印出的结果又变为了false

这是因为我们在正则串-^[a-z]{3}$的前后加入了起始^/终止$标识符;等价于要全匹配,才能返回true

我们改为Demo5样例,执行

public class Demo5 {
    public static void main(String[] args) {
        //[a-z]表示a~z之间的任何一个字符, {3}表示3个字符, 意思是匹配一个长度为3, 并且每个字符属于a~z的字符串
        Pattern p = Pattern.compile("^[a-z]{3}$"); // 正则串 - "^[a-z]{3}$"
        Matcher m = p.matcher("abc");  // 模式串 - "abcd"
        System.out.println(m.find());
    }
}

会发现现在的结果又变为了true,因为现在是匹配串和正则串是完全匹配的

分组匹配

以下是一段Cisco防火墙的配置

......
access-list 1 line 2 extended permit object TCP-33 object-group oa-ip object-group oa-test (hitcnt=0) 0x8f05ca88 
  access-list 1 line 2 extended permit tcp host 10.1.1.4 host 20.1.1.1 range 33 33 (hitcnt=0) 0xa1bf013a 
  access-list 1 line 2 extended permit tcp host 10.1.1.4 host 20.1.1.5 range 33 33 (hitcnt=0) 0x3372620c 
  access-list 1 line 2 extended permit tcp host 10.1.1.5 host 20.1.1.1 range 33 33 (hitcnt=0) 0xf907dcff 
  access-list 1 line 2 extended permit tcp host 10.1.1.5 host 20.1.1.5 range 33 33 (hitcnt=0) 0x73b10004 
  access-list 1 line 2 extended permit tcp host 10.1.1.1 host 20.1.1.1 range 33 33 (hitcnt=0) 0x964c8122 
  access-list 1 line 2 extended permit tcp host 10.1.1.1 host 20.1.1.5 range 33 33 (hitcnt=0) 0xf6fd9fc9 
  access-list 1 line 3 extended permit object TCP-33333 object WS-172.21.2.10_32 object WS-11.1.1.10_32 (hitcnt=0) (inactive) 0xb206976e 
  access-list 1 line 3 extended permit tcp host 172.21.2.10 host 11.1.1.10 range 33333 33333 (hitcnt=0) 0xb206976e 
access-list 2; 1 elements; name hash: 0x4ba440f6
access-list 2 line 1 extended permit ip any any (hitcnt=0) 0x86268c7e 
......

假设我们现在要查找其中access-list 1 line 3 extended permit object TCP-33333 object WS-172.21.2.10_32 object WS-11.1.1.10_32 (hitcnt=0) 0xb206976e 策略的HASH值0xb206976e

public void groutMatch() throws IOException {
        String raw = "上面Cisco防火墙的配置"
        String policy = "access-list 1 line 3 extended permit object TCP-33333 object WS-172.21.2.10_32 object WS-11.1.1.10_32";
    	// 转换特殊字符`.`
        String replacedPolicy = policy.replaceAll("\\.", "\\\\.");
        // 全匹配
//        String policyTemp = "^[\\s\\S]*?(policy)(\\s*?)(\\(\\S*?\\)\\s)(\\(\\S*?\\)\\s)*?(0x\\S+)[\\s\\S]*$";
    
        // 部分匹配
        String policyTemp = "(policy)(\\s*?)(\\(\\S*?\\)\\s)(\\(\\S*?\\)\\s)*?(0x\\S+)";
        policyTemp = policyTemp.replace("policy", replacedPolicy);
        Pattern CISCO_REGEX = Pattern.compile(policyTemp);
        Matcher matcher = CISCO_REGEX.matcher(raw);
        if (matcher.find()) {
            // matcher.group(index)
            System.out.print(matcher.group(1));
            System.out.print(matcher.group(2));
            System.out.print(matcher.group(3));
            System.out.print(StringUtils.isBlank(matcher.group(4))? "" : matcher.group(4));
            System.out.println(matcher.group(5));
        } else {
            System.out.println("not match");
        }
    }

matcher.group(index)其中group是针对括号()来说的,group(0)就是指的整个串,group(1) 指的是第一个括号里的东西,group(2)指的第二个括号里的东西。

对应上面的样例,我们可以拆解为如下:

***注意:***这里我们可以发现第3个括号中的正则表达式和第4个括号正则表达式的内容是一样的;理论上我们可以不要第3个括号的正则表达式因为第4个括号正则表达式后面跟随了*?可以匹配0-N个相同的正则表达式内容;但是由于当有多匹配*/+的时候,通过group(index)获取匹配内容的时候只能拿到最后一个匹配的内容;但是group(0)还是可以拿到匹配的所有内容

public void groutMatch() throws IOException {
        String raw = "上面Cisco防火墙的配置"
        String policy = "access-list 1 line 3 extended permit object TCP-33333 object WS-172.21.2.10_32 object WS-11.1.1.10_32";
    	// 转换特殊字符`.`
        String replacedPolicy = policy.replaceAll("\\.", "\\\\.");
    
        // 部分匹配
        String policyTemp = "(policy)(\s*?)(\(\S*?\)\s)*?(0x\S+)";
        policyTemp = policyTemp.replace("policy", replacedPolicy);
        Pattern CISCO_REGEX = Pattern.compile(policyTemp);
        Matcher matcher = CISCO_REGEX.matcher(raw);
        if (matcher.find()) {
            // matcher.group(index)
            System.out.print(matcher.group(1));
            System.out.print(matcher.group(2));
            System.out.print(matcher.group(3));
            System.out.print(StringUtils.isBlank(matcher.group(4))? "" : matcher.group(4));
            
            System.out.println();
            
            System.out.println(matcher.group(0));
        } else {
            System.out.println("not match");
        }
    }

输出如下结果:

access-list 1 line 3 extended permit object TCP-33333 object WS-172.21.2.10_32 object WS-11.1.1.10_32 (inactive) 0xb206976e

matcher.group(0)的输出结果

access-list 1 line 3 extended permit object TCP-33333 object WS-172.21.2.10_32 object WS-11.1.1.10_32 (hitcnt=0) (inactive) 0xb206976e