Java 正则表达式
Java - 正则表达式
Section titled “Java - 正则表达式”正则表达式(Regular expressions,简称 Regex)是定义搜索模式的字符序列。它们是进行模式匹配、搜索和文本操作的强大工具。
Java 提供了 java.util.regex 包来处理正则表达式。其语法与 Perl 大致兼容。
Pattern类: 表示一个编译后的正则表达式。编译模式通常是第一步,并且可能计算量较大,因此如果模式被重复使用,编译一次会更高效。Pattern对象是不可变(immutable)且线程安全(thread-safe)的。你可以使用静态方法compile()创建一个Pattern对象:Pattern p = Pattern.compile(regexString);Matcher类: 是一个引擎,用于使用Pattern对输入字符序列执行匹配操作。通过在Pattern对象上调用matcher(input)方法来获取Matcher对象:Matcher m = p.matcher(inputString);。Matcher对象是有状态的,并且不是线程安全的。PatternSyntaxException: 一个未受检异常(unchecked exception),在编译时指示正则表达式模式存在语法错误。
基本工作流程:
Section titled “基本工作流程:”- 将你的正则表达式定义为一个字符串。
- 使用
Pattern.compile()将 regex 字符串编译成一个Pattern对象。 - 使用
pattern.matcher(inputText)从Pattern和你的输入文本创建一个Matcher对象。 - 使用
Matcher的方法(例如find()、matches()、lookingAt()、group())执行匹配操作并检索结果。
捕获组(Capturing Groups)
Section titled “捕获组(Capturing Groups)”正则表达式中的括号 () 会创建捕获组。它们允许你:
- 将多个字符视为一个单元(例如,用于应用量词如
*、+、?)。 - 提取匹配文本的特定部分。
组从左到右根据其左括号进行编号,从 1 开始。组 0 总是表示整个匹配项。
示例:使用捕获组提取数据
Section titled “示例:使用捕获组提取数据”import java.util.regex.Matcher;import java.util.regex.Pattern;
public class RegexGroupsDemo { public static void main(String[] args) { String text = "User: alice ID: 101, User: bob ID: 205"; // 正则表达式用于查找 'User: (name)' 和 'ID: (number)' // (\w+) 捕获用户名(单词字符) // (\d+) 捕获 ID(数字) String regex = "User: (\\w+) ID: (\\d+)";
Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(text);
// 使用 find() 在文本中查找匹配项 while (matcher.find()) { System.out.println("完整匹配(组 0):" + matcher.group(0)); System.out.println("用户名(组 1): " + matcher.group(1)); System.out.println("用户 ID(组 2): " + matcher.group(2)); System.out.println("起始索引:" + matcher.start() + ", 结束索引:" + matcher.end()); System.out.println("---"); } }}预期输出:
Full match (Group 0): User: alice ID: 101Username (Group 1): aliceUser ID (Group 2): 101Start index: 0, End index: 19---Full match (Group 0): User: bob ID: 205Username (Group 1): bobUser ID (Group 2): 205Start index: 21, End index: 38---你可以使用 matcher.groupCount() 查找捕获组的数量(不包括组 0)。
正则表达式语法快速参考:
Section titled “正则表达式语法快速参考:”常见元字符(metacharacters)和构造:
| 语法 | 描述 |
|---|---|
. | 任何单个字符(换行符除外,除非设置了 DOTALL 标志) |
^ | 行的开头(或字符串开头,若设置了 MULTILINE 标志) |
$ | 行的结尾(或字符串结尾,若设置了 MULTILINE 标志) |
[...] | 字符类:匹配括号内的任何单个字符(例如,[abc]) |
[^...] | 否定字符类:匹配不在括号内的任何单个字符(例如,[^0-9]) |
A|B | 交替:匹配 A 或 B |
(...) | 捕获组 |
(?:...) | 非捕获组(分组字符但不捕获) |
\d | 数字:[0-9] |
\D | 非数字:[^0-9] |
\s | 空白字符:[ \t\n\x0B\f\r] |
\S | 非空白字符 |
\w | 单词字符:[a-zA-Z_0-9] |
\W | 非单词字符 |
\b | 单词边界 |
\B | 非单词边界 |
* | 量词:匹配前一个元素的零次或多次出现 |
+ | 量词:匹配前一个元素的一次或多次出现 |
? | 量词:匹配前一个元素的零次或一次出现 |
{n} | 量词:精确匹配 n 次出现 |
{n,} | 量词:至少匹配 n 次出现 |
{n,m} | 量词:至少匹配 n 次但不超过 m 次出现 |
*?, +?, ??, {n,}? 等 | 惰性/非贪婪量词:匹配最短可能的字符串 |
注意:反斜杠 (\) 在 Java 字符串字面量(String literals)中需要转义(例如,\d 变成 "\\d")。
关键 Matcher 方法:
Section titled “关键 Matcher 方法:”用于查找匹配项的方法:
Section titled “用于查找匹配项的方法:”| 方法 | 描述 |
|---|---|
boolean find() | 尝试查找输入序列中与模式匹配的下一个子序列(subsequence)。如果找到则返回 true,否则返回 false。常用于 while 循环。 |
boolean find(int start) | 重置匹配器并从指定索引开始搜索。 |
boolean matches() | 尝试将整个输入序列与模式进行匹配。仅当整个输入序列与模式匹配时才返回 true。 |
boolean lookingAt() | 尝试从输入序列的开头开始匹配模式。不要求整个输入序列都匹配。 |
用于检索匹配信息的方法:
Section titled “用于检索匹配信息的方法:”| 方法 | 描述 |
|---|---|
String group() or String group(0) | 返回上一次匹配所对应的输入子序列(完整匹配)。 |
String group(int group) | 返回在上一次匹配期间由给定组号捕获的子序列。 |
String group(String name) | 返回由给定命名组 (?<name>...) 捕获的子序列。 |
int groupCount() | 返回模式中的捕获组数量(不包括组 0)。 |
int start() or int start(0) | 返回上一次匹配的起始索引。 |
int start(int group) | 返回由给定组捕获的子序列的起始索引。 |
int end() or int end(0) | 返回上一次匹配的最后一个字符之后的偏移量。 |
int end(int group) | 返回由给定组捕获的最后一个字符之后的偏移量。 |
用于替换文本的方法:
Section titled “用于替换文本的方法:”| 方法 | 描述 |
|---|---|
String replaceAll(String replacement) | 将输入中所有与模式匹配的子序列替换为替换字符串。 |
String replaceFirst(String replacement) | 只替换第一个与模式匹配的子序列。 |
Matcher appendReplacement(StringBuffer sb, String replacement) or Matcher appendReplacement(StringBuilder sb, String replacement) | 将当前匹配项之前的部分文本以及替换文本追加到给定的缓冲区。允许进行复杂的替换,包括在替换字符串中使用捕获组($1、$2 等)。 |
StringBuffer appendTail(StringBuffer sb) or StringBuilder appendTail(StringBuilder sb) | 将输入序列的剩余部分(在最后一个匹配项之后的部分)追加到缓冲区。通常在 appendReplacement 循环之后使用。 |
示例:replaceAll 和 replaceFirst
Section titled “示例:replaceAll 和 replaceFirst”import java.util.regex.Matcher;import java.util.regex.Pattern;
public class RegexReplaceDemo { private static final String REGEX = "\b(cat|dog|fish)\b"; // 匹配完整的单词:cat, dog, 或 fish private static final String INPUT = "The quick brown fox jumps over the lazy dog. The cat naps."; private static final String REPLACE_ALL = "animal"; private static final String REPLACE_FIRST = "pet";
public static void main(String[] args) { Pattern p = Pattern.compile(REGEX); Matcher m = p.matcher(INPUT);
// 替换所有匹配项 String resultAll = m.replaceAll(REPLACE_ALL); System.out.println("Replace All: " + resultAll);
// 只替换第一个匹配项(需要新的匹配器或重置) m.reset(); // 将匹配器重置到输入的开头 String resultFirst = m.replaceFirst(REPLACE_FIRST); System.out.println("Replace First: " + resultFirst); }}预期输出:
Replace All: The quick brown fox jumps over the lazy animal. The animal naps.Replace First: The quick brown fox jumps over the lazy pet. The cat naps.示例:appendReplacement 和 appendTail(使用 StringBuilder)
Section titled “示例:appendReplacement 和 appendTail(使用 StringBuilder)”import java.util.regex.Matcher;import java.util.regex.Pattern;
public class RegexAppendDemo { private static final String REGEX = "(John|Jane)"; private static final String INPUT = "John Doe met Jane Smith. John works hard."; private static final String REPLACE_FORMAT = "**$1**"; // $1 指代第一个捕获组
public static void main(String[] args) { Pattern p = Pattern.compile(REGEX); Matcher m = p.matcher(INPUT); StringBuilder sb = new StringBuilder();
while (m.find()) { // 追加匹配项之前的文本和替换文本 m.appendReplacement(sb, REPLACE_FORMAT); } // 追加最后一个匹配项之后的任何剩余文本 m.appendTail(sb);
System.out.println(sb.toString()); }}预期输出:
**John** Doe met **Jane** Smith. **John** works hard.- 编译一次,重复使用: 编译
Pattern对象的开销较大。如果你多次使用相同的正则表达式,请编译一次并重复使用Pattern对象。 - 避免在热点循环(Hot Loops)中使用复杂正则表达式: 非常复杂的模式或编写低效的正则表达式可能会显著影响性能。
- 考虑替代方案: 对于简单的子字符串搜索或分割,标准的 String 方法(
contains()、indexOf()、split())可能比正则表达式更快。