正则表达式(Regular Expression,简称regex)是一种强大的文本处理工具,用于描述和匹配字符串模式。在Java编程中,正则表达式通过java.util.regex
包提供支持,使开发者能够高效地执行搜索、验证和替换等操作。无论是验证用户输入、解析日志文件,还是处理复杂文本数据,正则表达式都是不可或缺的工具。
Java的正则表达式引擎基于Perl 5语法,支持丰富的元字符(metacharacters),允许构建灵活且复杂的模式。本文将详细介绍Java正则表达式的语法、使用方法、进阶功能以及实用技巧,帮助开发者掌握这一核心技能。
Java正则表达式语法
正则表达式的核心是元字符,它们具有特殊含义,用于定义匹配规则。普通字符(如字母、数字)表示自身,而元字符(如^
、*
)则控制匹配行为。以下表格总结了Java正则表达式中常用的元字符及其功能:
元字符 | 匹配内容 | 说明 |
---|---|---|
^ | 行或字符串的开始 | 锚定匹配到字符串开头 |
$ | 行或字符串的结束 | 锚定匹配到字符串结尾 |
\b | 单词边界 | 匹配单词的开始或结束 |
\B | 非单词边界 | 匹配非单词边界的位置 |
\A | 整个字符串的开始 | 匹配字符串的绝对开头 |
\z | 整个字符串的结束 | 匹配字符串的绝对结尾 |
\Z | 整个字符串的结束(允许最后的行终止符) | 常用于多行文本 |
. | 任意一个字符(除行终止符) | 匹配除换行符外的任意字符 |
[...] | 字符类;匹配列出的任意一个字符 | 如[abc] 匹配a 、b 或c |
[^...] | 匹配未列出的任意一个字符 | 如[^abc] 匹配非a 、b 、c 的字符 |
(...) | 分组(捕获组) | 用于捕获匹配的部分以供后续使用 |
` | ` | 选择(或) |
(?:re) | 非捕获组 | 分组但不捕获 |
\G | 前一个匹配的结束位置 | 用于连续匹配 |
\n | 反向引用第n个捕获组 | 引用之前捕获的内容 |
{m,n} | 重复m到n次 | 指定重复次数范围 |
{m,} | 至少重复m次 | |
{m} | 恰好重复m次 | |
*, +, ? | 量词 | 分别表示0次或多次、1次或多次、0次或1次 |
*?, +?, ?? | 勉强量词 | 匹配尽可能少的字符 |
*+, ++, ?+ | 占有量词 | 匹配尽可能多的字符,不回溯 |
\ | 转义字符 | 使元字符表示字面字符,如\. 匹配. |
\Q...\E | 引用 | 将中间内容视为字面字符 |
\t, \r, \n, \f | 制表符、回车、换行、换页 | 表示特殊字符 |
\w | 单词字符 | [a-zA-Z_0-9] |
\W | 非单词字符 | |
\d | 数字 | [0-9] |
\D | 非数字 | |
\s | 空白字符 | 包括空格、制表符等 |
\S | 非空白字符 | |
\p{InGreek} | Greek块中的字符 | Unicode块支持 |
\p{Lu} | 大写字母 | Unicode属性支持 |
\p{Alnum} | 字母数字字符 | [A-Za-z0-9] |
\p{Space} | 空白字符 | [ \t\n\x0B\f\r] |
\p{Punct} | 标点符号 | 包括!"#$%&'()*+,-./:;<=>?@[\]^_ { |
这些元字符可以组合使用,构建复杂的模式。例如:
a+
:匹配一个或多个a
。Mrs?\.
:匹配Mr.
或Mrs.
。\d{2,3}
:匹配2到3位数字。
使用Java正则表达式
在Java中,正则表达式通过Pattern
和Matcher
类实现。以下是典型的使用流程:
1. 编译模式
使用Pattern.compile(String regex)
将正则表达式字符串编译为Pattern
对象。可以指定标志,如Pattern.CASE_INSENSITIVE
进行不区分大小写的匹配。
Pattern pattern = Pattern.compile("\\d+", Pattern.CASE_INSENSITIVE);
2. 创建匹配器
通过Pattern.matcher(CharSequence input)
为输入字符串创建Matcher
对象。
Matcher matcher = pattern.matcher("123 abc 456");
3. 执行匹配操作
Matcher
类提供多种方法:
matches()
:检查整个字符串是否匹配模式。find()
:查找下一个匹配的子串。lookingAt()
:从字符串开头尝试匹配。replaceAll(String replacement)
:替换所有匹配的部分。split(String regex)
:根据模式分割字符串。
实用示例
以下是一些常见的正则表达式应用场景及其Java实现:
1. 完整匹配
验证美国社会保险号(SSN,格式为xxx-xx-xxxx
):
String pattern = "^\\d{3}-\\d{2}-\\d{4}$";
String text = "123-45-6789";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(text);
if (m.matches()) {System.out.println("有效SSN");
} else {System.out.println("无效SSN");
}
2. 查找子串
提取文本中的所有单词:
String pattern = "\\b\\w+\\b";
String text = "Hello world, how are you?";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(text);
while (m.find()) {System.out.println("单词: " + m.group());
}
输出:
单词: Hello
单词: world
单词: how
单词: are
单词: you
3. 捕获组
提取SSN的各部分(区号、交换码、序号):
String pattern = "(\\d{3})-(\\d{2})-(\\d{4})";
String text = "SSN: 123-45-6789";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(text);
if (m.find()) {System.out.println("区号: " + m.group(1));System.out.println("交换码: " + m.group(2));System.out.println("序号: " + m.group(3));
}
输出:
区号: 123
交换码: 45
序号: 6789
4. 替换文本
将电话号码替换为掩码格式:
String text = "Contact me at 123-456-7890 or 987-654-3210.";
String pattern = "\\d{3}-\\d{3}-\\d{4}";
String replaced = text.replaceAll(pattern, "XXX-XXX-XXXX");
System.out.println(replaced);
输出:
Contact me at XXX-XXX-XXXX or XXX-XXX-XXXX.
5. 分割字符串
按非字母字符分割文本:
String text = "apple,banana;cherry|date";
String[] fruits = text.split("[,;]|\\|");
for (String fruit : fruits) {System.out.println(fruit.trim());
}
输出:
apple
banana
cherry
date
6. 复杂模式匹配
匹配以T
开头、紧跟元音、后接任意单词字符并以!
结尾的字符串:
String pattern = "^T[aeiou]\\w*!$";
String text = "Triumphant!";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(text);
if (m.matches()) {System.out.println("匹配成功");
}
进阶主题
1. 贪婪 vs. 勉强量词
量词分为贪婪、勉强和占有三种模式:
- 贪婪:匹配尽可能多的字符(默认)。
- 勉强:匹配尽可能少的字符(加
?
)。 - 占有:匹配尽可能多的字符且不回溯(加
+
)。
例如,匹配HTML标签:
String text = "<b>bold</b> <i>italic</i>";
Pattern greedy = Pattern.compile("<.*>");
Matcher mGreedy = greedy.matcher(text);
if (mGreedy.find()) {System.out.println("贪婪匹配: " + mGreedy.group()); // <b>bold</b> <i>italic</i>
}Pattern reluctant = Pattern.compile("<.*?>");
Matcher mReluctant = reluctant.matcher(text);
while (mReluctant.find()) {System.out.println("勉强匹配: " + mReluctant.group()); // <b>, </b>, <i>, </i>
}
2. Unicode支持
Java正则表达式支持Unicode字符和属性。例如,\p{L}
匹配任何Unicode字母:
String pattern = "\\p{L}+";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher("Hello 世界");
while (m.find()) {System.out.println(m.group()); // Hello, 世界
}
这对于处理多语言文本尤为有用。
实用技巧
1. 双重转义反斜杠
在Java字符串中,正则表达式的反斜杠需要双重转义。例如,匹配数字的\d
需写作"\\d"
:
String pattern = "\\d+"; // 正确
// String pattern = "\d+"; // 错误:编译失败
2. 使用文本块(Java 14+)
Java 14引入了文本块,简化了多行正则表达式的编写:
String pattern = """\\d{3}-\\d{2}-\\d{4}""";
Pattern p = Pattern.compile(pattern);
这减少了转义引号的麻烦。
3. 测试工具
- REDemo:一个交互式正则表达式测试工具,允许实时测试模式和字符串匹配。源代码可在darwinsys-api GitHub仓库找到。
- 在线工具:如regex101,支持Java正则表达式语法,提供实时调试和解释功能。
4. 使用标志优化匹配
Pattern.compile()
支持多种标志:
Pattern.CASE_INSENSITIVE
:不区分大小写。Pattern.MULTILINE
:使^
和$
匹配每行的开始和结束。Pattern.DOTALL
:使.
匹配包括换行符在内的所有字符。
例如:
Pattern p = Pattern.compile("^hello$", Pattern.MULTILINE);
Matcher m = p.matcher("hello\nworld\nhello");
while (m.find()) {System.out.println("找到: " + m.group());
}
结论
Java正则表达式为开发者提供了处理文本的强大工具,能够简化数据验证、文本解析和字符串操作等任务。通过掌握元字符语法、Pattern
和Matcher
类的使用方法,以及进阶功能如捕获组和Unicode支持,开发者可以编写更高效、灵活的代码。建议通过实践和使用测试工具(如REDemo或regex101)加深理解,并探索正则表达式的更多可能性。