在Java中使用原生JDK实现正则表达式的匹配,不可避免的我们需要使用到
java.util.regex
包下的两个类Pattern
和Matcher
初识模式匹配
我们的一般用法,就是先定义一个正则串
和匹配串
,然后看匹配串
中是否有满足正则串
条件的字符序列;如下:
java
代码解读
复制代码
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样例
java
代码解读
复制代码
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()
来判断是否部分匹配
到结果
整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】即可免费获取
java
代码解读
复制代码
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
起始/终止标识符
java
代码解读
复制代码
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样例
,执行
java
代码解读
复制代码
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防火墙的配置
tex
代码解读
复制代码
...... 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
java
代码解读
复制代码
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)
还是可以拿到匹配的所有内容
java
代码解读
复制代码
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