Regex 入门指南:如何在 Java 中优雅地处理字符串
在编程的世界里,处理字符串是家常便饭。无论是校验用户输入的邮箱格式,还是从杂乱的日志中提取关键信息,字符串操作都无处不在。
如果你还在用一堆复杂的 indexOf、substring 和 if-else 嵌套来处理文本,那么是时候掌握正则表达式 (Regular Expressions) 这个强大的武器了。
什么是正则表达式?
正则表达式(简称 Regex)是一种用于匹配字符串中字符组合的模式(Pattern)。你可以把它想象成一种**“超级查找与替换”**的规则语言。
虽然它的语法看起来像乱码(比如 ^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$),但一旦掌握了它,你就能用一行代码解决几十行逻辑才能完成的任务。
核心语法速查表
在深入 Java 代码之前,我们需要先了解 Regex 的通用符号。
1. 元字符 (Metacharacters)
.:匹配除换行符以外的任意字符。^:匹配字符串的开始。$:匹配字符串的结束。
2. 字符类 (Character Classes)
\d:匹配数字 (0-9)。\w:匹配字母、数字或下划线 (Word character)。\s:匹配空白字符(空格、Tab、换行)。[abc]:匹配方括号内的任意一个字符(a 或 b 或 c)。[^abc]:匹配除了方括号内字符以外的任意字符。
3. 量词 (Quantifiers)
*:出现 0 次或多次。+:出现 1 次或多次。?:出现 0 次或 1 次。{n}:精确出现 n 次。{n,m}:出现 n 到 m 次。
⚠️ Java 中的特殊注意事项 在 Java 字符串中,反斜杠
\是转义字符。因此,当你要表示 Regex 中的\d时,必须写成"\\d"。
Java 中的 Regex 实战
Java 在 java.util.regex 包中提供了两个核心类来处理正则:
Pattern:正则表达式的编译表示(规则)。Matcher:对输入字符串进行解释和匹配操作的引擎(执行者)。
场景一:数据校验 (Validation)
这是最常见的场景,例如检查手机号是否合法。
import java.util.regex.Pattern;import java.util.regex.Matcher;
public class RegexDemo { public static void main(String[] args) { // 规则:以1开头,第二位是3-9,后面接9位数字 String regex = "^1[3-9]\\d{9}$"; String phoneNumber = "13800138000";
// 方法1:直接使用 String 类的 helper 方法(适合简单的一次性检查) boolean isValidSimple = phoneNumber.matches(regex); System.out.println("Simple Check: " + isValidSimple);
// 方法2:使用 Pattern 和 Matcher(适合高性能或复杂逻辑) Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(phoneNumber);
// matches() 要求整个字符串完全匹配 if (matcher.matches()) { System.out.println("这是一个有效的手机号"); } else { System.out.println("手机号格式错误"); } }}场景二:信息提取 (Extraction)
假设我们有一段日志,需要从中提取订单号。
文本: [INFO] User created order: ORD-20230521-X99 in system.
目标: 提取 ORD-20230521-X99
import java.util.regex.Pattern;import java.util.regex.Matcher;
public class ExtractionDemo { public static void main(String[] args) { String logEntry = "[INFO] User created order: ORD-20230521-X99 in system.";
// 使用圆括号 () 创建捕获组 // 规则:匹配 "ORD-" 开头,后面跟任意非空字符 String regex = "(ORD-[\\w-]+)";
Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(logEntry);
// find() 用于查找子串,不同于 matches() if (matcher.find()) { // group(0) 是整个匹配到的串 // group(1) 是第一个括号内匹配到的内容 String orderId = matcher.group(1); System.out.println("提取到的订单号: " + orderId); } }}场景三:文本替换与脱敏 (Replacement)
在保护隐私时,我们经常需要把敏感信息(如身份证、银行卡)中间部分替换为星号。
public class ReplaceDemo { public static void main(String[] args) { String input = "My credit card is 4555-1234-5678-9012, please charge it.";
// 规则:匹配四组数字,中间可能有连字符 // 我们想保留前4位和后4位,中间替换为 **** // 使用分组:$1 代表第一组,$2 代表第二组... String regex = "(\\d{4})-\\d{4}-\\d{4}-(\\d{4})";
// 替换逻辑:保留第一组和最后一组,中间硬编码为 ****-**** String result = input.replaceAll(regex, "$1-****-****-$2");
System.out.println(result); // 输出: My credit card is 4555-****-****-9012, please charge it. }}性能优化建议
在 Java 中使用 Regex 时,有一个常见的性能陷阱。
如果你在一个循环中频繁调用 String.matches(...) 或 Pattern.compile(...),性能会非常差,因为每次调用都会重新编译正则表达式。
使用建议:
将 Pattern 定义为 static final 常量,只编译一次,重复使用。
public class UserValidator { // 预编译 Pattern,提升性能 private static final Pattern EMAIL_PATTERN = Pattern.compile("^[\\w.-]+@[\\w.-]+\\.[a-z]{2,}$");
public boolean validateEmail(String email) { if (email == null) return false; return EMAIL_PATTERN.matcher(email).matches(); }}调试正则
复杂正则可以先在 Regex101 中测试,并为边界输入补充单元测试。表达式过长或需要解析嵌套结构时,普通字符串处理或专用解析器通常更容易维护。
