在 Java 编程中,我们经常需要处理字符串的匹配和替换操作。为了便捷地实现这些功能,Java 提供了 Pattern 类和 Matcher 类。Pattern 类用于定义正则表达式模式,而 Matcher 类用于在给定的输入字符串中进行匹配操作。
本文将介绍 Pattern 类和 Matcher 类在实际场景中常见的应用,以及列举一些常见的正则表达式示例。
基本定义
Java 正则表达式通过 java.util.regex
包下的 Pattern 类与 Matcher 类实现
Pattern 类的实例适应正则表达式的编译表示形式,指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建 Matcher 对象,根据正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都会保留在匹配器中,因此多个匹配器可以共享同一模式。
因此,典型的调用顺序是:
Pattern.compile()
生成编译后的正则表达式matcher()
生成匹配器matches()
执行匹配操作,并记录下匹配结果状态
单独用 Pattern 只能使用 Pattern.matches(String regex,CharSequence input)
这一种最基础最简单的匹配。
这种匹配的模式,只能满足一次性使用,不能重复使用,因此它的效率不高。但是从另一个角度来看,此类的实例是不可变的,可供多个并发线程安全使用。相比而言,Matcher
类的实例在并发下则没那么安全了。
Pattern 类
Pattern 类是 java.util.regex
包中的一个类,它用于定义正则表达式模式。正则表达式是一种强大的文本匹配工具,它可以用于在字符串中查找、替换或提取特定模式的文本。
Pattern 类用于创建一个正则表达式,也可以说创建一个匹配模式。它的构造方法是私有的,不可以直接创建,但可以通过静态方法 Pattern.complie(String regex)
这个简单工厂方法编译一个正则表达式模式。编译后的 Pattern 对象可以用于创建 Matcher 对象,后者用于在输入字符串中执行匹配操作。
java
Pattern p = Pattern.compile("\\w+");
如果直接使用 matches 静态方法,则对应返回一个 boolean 结果,表示是否匹配上目标表达式:
java
Pattern.matches("\\d+", "154234");
Matcher 类
Matcher类是Pattern类的内部类,用于在给定的输入字符串中执行匹配操作。Matcher对象可以通过调用Pattern对象的 matcher()
方法来创建,然后可以使用各种方法执行匹配操作。
java
Matcher m = Pattern.compile("\\w+").matcher("");
得到 Matcher 实例后,可以通过一系列的方法进行操作:
find()
:判断是否有下一组匹配成功的结果lookingAt()
:起始位置是否匹配成功group()
:等价于group(0)
,返回匹配整个表达式的结果start()
:等价于start(0)
,返回整个结果的起始下标(包含)end()
:等价于end(0)
,返回整个结果的末尾下标(不包含)
下面给出一段实例代码:
java
@Test
public void testMatchSinglePattern() {
String s1 = "vnkjslifgoia123aoivcmaiosdjfaio456vmldfjviodf789avoasdmfoi010";
String s2 = "000vnkjslifgoia123aoivcmaiosdjfaio456vmldfjviodf789avoasdmfoi010";
Pattern p = Pattern.compile("\\d+");
Matcher m1 = p.matcher(s1);
Matcher m2 = p.matcher(s2);
// 包含字母,全匹配失败
Assert.assertFalse(m1.matches());
Assert.assertFalse(m2.matches());
// 存在数字,有匹配的部分
Assert.assertTrue(m2.find());
// 开头部分是否匹配成功
Assert.assertFalse(m1.lookingAt());
Assert.assertTrue(m2.lookingAt());
// 分别取出匹配的部分,每执行一次find()方法,获取一次下一个匹配项的信息
if (m1.find()) {
Assert.assertEquals(m1.group(), "123");
Assert.assertEquals(m1.start(), 12);
Assert.assertEquals(m1.end(), 15);
}
if (m1.find()) {
Assert.assertEquals(m1.group(), "456");
Assert.assertEquals(m1.start(), 31);
Assert.assertEquals(m1.end(), 34);
}
}
捕获组 group()
当匹配的正则表达式模式通过括号 ()
分组创建了多个匹配单元时,成功匹配的结果则成为捕获组。对于一个 Matcher 对象匹配字符串 s 的结果来说, group()
或 group(0)
对应匹配整个表达式,m.group()
的结果与 s.substring(m.start(), m.end())
的结果是等价的。
通过 groupCount()
可以返回当前捕获组的数量,可以通过 group(1)
、 group(2)
依次取出每个子单元匹配的内容。
start()
、 end()
方法,则对应获取匹配内容的起始下标和末尾下标(与字符串一样,遵循"包头不包尾"原则)。类似的,start(1)
、 end(1)
、start(2)
、 end(2)
则是获取每个单元捕获组的首尾下标
下面给出一个例子来进行说明:
java
@Test
public void testMatchMultiPattern() {
String s = "aaa111AAA bbb222 cccccc33CCCCCC ";
// 两组匹配内容,用括号()封装
Pattern p = Pattern.compile("([a-z]+)(\\d+)([A-Z]+)");
Matcher m = p.matcher(s);
if (m.find()) {
// 匹配模式中有三个()子序列,因此捕获组内count为3
Assert.assertEquals(m.group(), "aaa111AAA");
Assert.assertEquals(m.group(0), "aaa111AAA");
Assert.assertEquals(m.groupCount(), 3);
Assert.assertEquals(m.group(1), "aaa");
Assert.assertEquals(m.group(2), "111");
Assert.assertEquals(m.group(3), "AAA");
Assert.assertEquals(m.start(), 0);
Assert.assertEquals(m.start(1), 0);
Assert.assertEquals(m.start(2), 3);
Assert.assertEquals(m.start(3), 6);
Assert.assertEquals(m.end(), 9);
Assert.assertEquals(m.end(1), 3);
Assert.assertEquals(m.end(2), 6);
Assert.assertEquals(m.end(3), 9);
}
if (m.find()) {
Assert.assertEquals(m.group(), "cccccc33CCCCCC");
Assert.assertEquals(m.group(0), "cccccc33CCCCCC");
Assert.assertEquals(m.groupCount(), 3);
Assert.assertEquals(m.group(1), "cccccc");
Assert.assertEquals(m.group(2), "33");
Assert.assertEquals(m.group(3), "CCCCCC");
Assert.assertEquals(m.start(), 17);
Assert.assertEquals(m.start(1), 17);
Assert.assertEquals(m.start(2), 23);
Assert.assertEquals(m.start(3), 25);
Assert.assertEquals(m.end(), 31);
Assert.assertEquals(m.end(1), 23);
Assert.assertEquals(m.end(2), 25);
Assert.assertEquals(m.end(3), 31);
}
}
范围匹配 region
region 用于设定查找范围,设定之后之前查找保存在 mathcer 中的结果清空,随后调用 find()
可在指定的下标范围中进行匹配。
java
@Test
public void testMatcherRegion() {
String s = "aaa111AAACCCCCC ";
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(s);
m.region(0, 3);
Assert.assertFalse(m.find());
m.region(4, 10);
m.find();
Assert.assertEquals(m.group(), "11");
}
重置匹配 reset
顾名思义,reset 重置 matcher 实例中的匹配结果,重置后匹配记录被清空,需要再次调用 find()
方法重新进行匹配。
java
@Test
public void testMatcherReset() {
String s = "aaa111AAA bbb222 cccccc33CCCCCC ";
// 两组匹配内容,用括号()封装
Pattern p = Pattern.compile("[a-z]+");
Matcher m = p.matcher(s);
if (m.find()) {
Assert.assertEquals(m.group(), "aaa");
}
if (m.find()) {
Assert.assertEquals(m.group(), "bbb");
}
m.reset();
if (m.find()) {
Assert.assertEquals(m.group(), "aaa");
}
}
其他常用的字符串操作
字符串分割 split
java
@Test
public void testStringSplit() {
String s = "Good morning, ladies and gentleman!";
// 按空格分割字符串,获取各个单词
Pattern spacePattern = Pattern.compile("\\s+");
String[] arr = spacePattern.split(s);
Assert.assertEquals(arr[0], "Good");
Assert.assertEquals(arr[1], "morning,");
Assert.assertEquals(arr[2], "ladies");
Assert.assertEquals(arr[3], "and");
Assert.assertEquals(arr[4], "gentleman!");
}
字符串替换 replace
java
@Test
public void testStringReplacement() {
String s = "apple-red,banana-yellow,grass-green,sky-blue";
Pattern p = Pattern.compile("-");
Assert.assertEquals(p.matcher(s).replaceAll(":"), "apple:red,banana:yellow,grass:green,sky:blue");
Assert.assertEquals(p.matcher(s).replaceFirst("->"), "apple->red,banana-yellow,grass-green,sky-blue");
}
字符串匹配拼接 append
主要包括 appendReplacement()
和 appendTail()
,两个方法执行的动作,简单来说:
appendReplacement()
将本次查找匹配到的字符串及其之前的字符追加到一个StringBuffer
中去。appendTail()
是将匹配后面剩下的未匹配的字符串追加到一个StringBuffer
中。
java
@Test
public void testStringBuilderAppend(){
Pattern pattern = Pattern.compile("a|b");
Matcher matcher = pattern.matcher("aiiiibqqqqqafffffff@here.com");
StringBuffer sb1 = new StringBuffer();
StringBuffer sb2 = new StringBuffer();
if (matcher.find()) {
matcher.appendReplacement(sb1, matcher.group());
Assert.assertEquals(sb1.toString(), "a");
}
if (matcher.find()) {
matcher.appendReplacement(sb1, matcher.group());
Assert.assertEquals(sb1.toString(), "aiiiib");
}
if (matcher.find()) {
matcher.appendReplacement(sb1, matcher.group());
Assert.assertEquals(sb1.toString(), "aiiiibqqqqqa");
}
matcher.appendTail(sb2);
Assert.assertEquals(sb2.toString(), "fffffff@here.com");
}