相关链接:
- 使用Java正则表达式匹配器查找最后一个匹配项 | 码农家园
- Java正则表达式的完全匹配与部分匹配_烟雨青山的博客-CSDN博客_java 正则表达式完全匹配
- 正则表达式在Java中的使用 - 掘金
- 正则表达式matcher.group用法 - SummerChill - 博客园
本文不讲解正则表达式相关的知识内容,如对这块知识不太了解的同学,请先移步至正则表达式 – 教程 | 菜鸟教程学习相关知识;本文主要讲解在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
- 如果要深究正则表达式背后的原理, 会涉及编译原理中自动机等知识, 此处不展开描述. 为了达到通俗易懂, 这里用较为形象的语言描述.
Pattern
可以理解为一个模式, 字符串需要与某种模式进行匹配. 比如Demo2
中, 我们定义的模式是一个长度为3的字符串, 其中每个字符必须是a~z中的一个
.- 我们看到创建
Pattern
对象时调用的是Pattern
类中的compile
方法, 也就是说对我们传入的正则表达式编译后得到一个模式对象. 而这个经过编译后模式对象, 会使得正则表达式使用效率会大大提高, 并且作为一个常量, 它可以安全地供多个线程并发使用. Matcher
可以理解为模式匹配某个字符串后产生的结果. 字符串和某个模式匹配后可能会产生很多个结果, 这个会在后面的例子中讲解.- 最后当我们调用
m.matches()
时就会返回完整字符串与模式匹配的结果 - 上面的三行代码可以简化为一行代码
System.out.println("abc".matches("[a-z]{3}"));
- 但是如果一个正则表达式需要被重复匹配, 这种写法效率较低.
完全匹配和部分匹配
通过上面的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