String 正则方法:能链式调用的简洁 API
很多场景其实不需要 Pattern 和 Matcher 那么完整的工具链,直接用 String 自带的方法就够了。
关键是:简单场景用 String 方法,复杂场景再上 Pattern/Matcher。
五个核心方法
| 方法 | 作用 | 返回值 |
|---|---|---|
matches(regex) | 验证整个字符串是否匹配 | boolean |
replaceAll(regex, replacement) | 替换所有匹配的子串 | String |
replaceFirst(regex, replacement) | 替换第一个匹配的子串 | String |
split(regex) | 按正则分割字符串 | String[] |
split(regex, limit) | 限制分割次数 | String[] |
matches():快速验证
验证 vs 误判
java
public class MatchesTrapDemo {
public static void main(String[] args) {
// 场景:验证手机号
String phone = "13812345678";
// ❌ 误判!没加 ^ 和 $
// "abc13812345678".matches("1[3-9]\\d{9}") 返回 true
String badPattern = "1[3-9]\\d{9}";
System.out.println("不加锚点: " + "abc13812345678".matches(badPattern)); // true!误判!
// ✅ 正确:加 ^ 和 $
String goodPattern = "^1[3-9]\\d{9}$";
System.out.println("加锚点: " + phone.matches(goodPattern)); // true
System.out.println("加锚点: " + "abc13812345678".matches(goodPattern)); // false
}
}常见验证场景
java
public class ValidationScenarios {
public static void main(String[] args) {
// 手机号(中国大陆 11 位)
String phone = "13812345678";
System.out.println("手机号: " + phone.matches("^1[3-9]\\d{9}$"));
// 邮箱(简化版)
String email = "user@example.com";
System.out.println("邮箱: " + email.matches("^\\w+@\\w+\\.\\w+$"));
// 整数
String num = "-12345";
System.out.println("整数: " + num.matches("^-?\\d+$"));
// 正整数
String posNum = "12345";
System.out.println("正整数: " + posNum.matches("^\\d+$"));
// 浮点数
String decimal = "-3.14";
System.out.println("浮点数: " + decimal.matches("^-?\\d+\\.\\d+$"));
// 邮政编码(6 位)
String zip = "100000";
System.out.println("邮编: " + zip.matches("^\\d{6}$"));
// URL
String url = "https://example.com/path";
System.out.println("URL: " + url.matches("^https?://\\S+$"));
}
}replaceAll / replaceFirst:文本替换
Before → After 对比
java
public class ReplaceBeforeAfter {
public static void main(String[] args) {
String text = "订单号: A123, 金额: 199元, 订单号: B456, 金额: 299元";
// 去除所有数字
String noDigits = text.replaceAll("\\d+", "#");
System.out.println("去除数字: " + noDigits);
// Before: 订单号: A123, 金额: 199元, 订单号: B456, 金额: 299元
// After: 订单号: A#, 金额: #元, 订单号: B#, 金额: #元
// 去除所有非数字
String onlyDigits = text.replaceAll("\\D+", "");
System.out.println("只保留数字: " + onlyDigits);
// Before: 订单号: A123, 金额: 199元, 订单号: B456, 金额: 299元
// After: 123199456299
// 只替换第一个
String first = text.replaceFirst("订单号:", "单号:");
System.out.println("替换第一个: " + first);
// Before: 订单号: A123, 金额: 199元, 订单号: B456, 金额: 299元
// After: 单号: A123, 金额: 199元, 订单号: B456, 金额: 299元
}
}反向引用:替换时复用匹配内容
java
public class ReplaceBackRef {
public static void main(String[] args) {
// 场景1:格式化手机号 13812345678 → 138-1234-5678
String phone = "13812345678";
String formatted = phone.replaceFirst("(\\d{3})(\\d{4})(\\d{4})", "$1-$2-$3");
System.out.println("手机格式化: " + formatted);
// 场景2:手机号脱敏 13812345678 → 138****5678
String masked = phone.replaceFirst("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
System.out.println("手机脱敏: " + masked);
// 场景3:驼峰转下划线 userName → user_name
String camel = "userNameFirst";
String snake = camel.replaceAll("([A-Z])", "_$1").toLowerCase();
System.out.println("驼峰转下划线: " + snake);
// 场景4:下划线转驼峰 user_name → userName
String underscore = "user_name_first";
String camelBack = underscore.replaceAll("_([a-z])", m -> m.group(1).toUpperCase());
System.out.println("下划线转驼峰: " + camelBack);
// 场景5:交换键值对 name=Alice → Alice=name
String kv = "name=Alice";
String swapped = kv.replaceAll("(\\w+)=(\\w+)", "$2=$1");
System.out.println("交换键值: " + swapped);
}
}清理空白
这是最常见的文本预处理操作。
java
public class WhitespaceCleanup {
public static void main(String[] args) {
// 合并多个空白为单个空格
String messy = " 多个 空白 ";
String cleaned = messy.replaceAll("\\s+", " ").trim();
System.out.println("清理空白: '" + cleaned + "'");
// Before: " 多个 空白 "
// After: "多个 空白"
// 去除所有空白
String noSpaces = "hello world".replaceAll("\\s+", "");
System.out.println("去全部空白: " + noSpaces); // helloworld
// 去除多余空格(单词间保留一个)
String text = "Hello World\t\nJava";
String normalized = text.replaceAll("\\s+", " ");
System.out.println("标准化: " + normalized); // Hello World Java
}
}split():字符串分割
基本用法
java
public class SplitBasic {
public static void main(String[] args) {
// 按逗号分割
String csv = "apple,banana,cherry";
String[] fruits = csv.split(",");
System.out.println("逗号: " + Arrays.toString(fruits));
// [apple, banana, cherry]
// 多分隔符
String mixed = "apple,banana;cherry:date";
String[] multi = mixed.split("[,;:]");
System.out.println("多分隔符: " + Arrays.toString(multi));
// [apple, banana, cherry, date]
// 按空白分割(空格、Tab、换行都行)
String spaced = "a b\tc\nd";
String[] bySpace = spaced.split("\\s+");
System.out.println("空白分割: " + Arrays.toString(bySpace));
// [a, b, c, d]
}
}容易踩的坑
java
public class SplitTrap {
public static void main(String[] args) {
// 坑1:连续分隔符
String doubleComma = "a,,b,,,c";
System.out.println("默认忽略连续: " + Arrays.toString(doubleComma.split(",")));
// [a, b, c] - 中间的空串被忽略了
// 坑2:尾部空串
String trailing = "a,b,";
System.out.println("默认丢弃尾部: " + Arrays.toString(trailing.split(",")));
// [a, b] - 尾部的空串被丢弃
// 坑3:头部空串
String leading = ",a,b";
System.out.println("保留头部: " + Arrays.toString(leading.split(",")));
// [, a, b] - 头部的空串被保留了
// 解决方案:传 limit = -1
System.out.println("保留所有空: " + Arrays.toString(trailing.split(",", -1)));
// [a, b, ] - 尾部空串保留了
// 场景:解析 CSV 时,要保留尾部空串
String csvLine = "a,b,c,";
String[] fields = csvLine.split(",", -1);
System.out.println("CSV 字段: " + fields.length + " 个"); // 4 个
}
}限制分割次数
java
public class SplitLimit {
public static void main(String[] args) {
String path = "/usr/local/bin/java";
// 不限制:全部分割
String[] all = path.split("/");
System.out.println("全部分割: " + Arrays.toString(all));
// [, usr, local, bin, java]
// 限制次数:只分割前 n-1 次
String[] limited = path.split("/", 2);
System.out.println("限制2: " + Arrays.toString(limited));
// [, usr/local/bin/java] - 只在第一个 / 处分割
// 实际场景:提取文件名
String filename = path.split("/")[3];
System.out.println("文件名: " + filename); // java
// 实际场景:提取父路径
String parent = path.split("/(?=[^/]+$)", 2)[0];
System.out.println("父路径: " + parent); // /usr/local/bin
}
}链式调用:组合多个操作
文本预处理流水线
java
public class ChainedDemo {
public static void main(String[] args) {
String input = " 姓名: Alice, 年龄: 25, 城市: Beijing ";
// 链式处理
String result = input
.trim() // 去首尾空白
.replaceAll("\\s+", " ") // 合并多余空白
.replaceAll("\\w+:", "") // 移除键名
.replaceAll("[,:]", "") // 移除冒号逗号
.trim(); // 再去一遍首尾
System.out.println("处理后: '" + result + "'");
// "Alice 25 Beijing"
// 实战:从日志提取有效信息
String log = "2026-03-22 ERROR [Service] 用户登录失败,原因: 密码错误";
String info = log
.replaceFirst("^\\d{4}-\\d{2}-\\d{2}\\s+\\w+\\s+\\[\\w+\\]\\s*", "")
.replaceFirst(",原因:.*$", "");
System.out.println("提取信息: " + info);
// "用户登录失败"
}
}提取 + 过滤
java
public class ExtractAndFilter {
public static void main(String[] args) {
String text = "邮箱1: test@example.com, 邮箱2: invalid-email, 邮箱3: user@domain.org";
// 提取所有邮箱
String[] parts = text.split("[,\\s]+");
for (String part : parts) {
if (part.matches("\\w+@\\w+\\.\\w+")) {
System.out.println("有效邮箱: " + part);
}
}
// 更优雅的方式:先清理,再提取
String emails = text.replaceAll("[^\\w@.,\\s]", "");
Arrays.stream(emails.split("[,\\s]+"))
.filter(s -> s.contains("@"))
.forEach(System.out::println);
}
}实战:从真实数据中提取信息
日志解析
java
public class LogParsing {
public static void main(String[] args) {
String accessLog = "192.168.1.100 - - [22/Mar/2026:10:30:15 +0800] \"GET /api/users HTTP/1.1\" 200 1234";
// 提取 IP
String ip = accessLog.replaceAll("^([\\d.]+).*$", "$1");
System.out.println("IP: " + ip);
// 提取时间
String time = accessLog.replaceAll(".*\\[([^\\]]+)\\].*", "$1");
System.out.println("时间: " + time);
// 提取路径
String path = accessLog.replaceAll(".*\"([^\"]+)\".*", "$1");
System.out.println("请求: " + path);
// 提取状态码
String status = accessLog.replaceAll(".*\"\\s+(\\d+)\\s+.*", "$1");
System.out.println("状态: " + status);
}
}数据清洗
java
public class DataCleaning {
public static void main(String[] args) {
// 场景:从网页文本提取价格
String productPage = "【正品保障】iPhone 15 Pro Max 原价 ¥5999.00,双十一特惠价 ¥4999.00起!";
// 提取所有价格
String prices = productPage.replaceAll(".*?([¥¥]?\\d+\\.\\d{2}).*", "$1");
System.out.println("提取价格: " + prices);
// 清理数字(去掉货币符号)
String cleanPrice = "¥4999.00".replaceAll("[^\\d.]+", "");
System.out.println("纯数字: " + cleanPrice);
// 格式化输出
double price = Double.parseDouble(cleanPrice);
System.out.println("格式化: ¥" + String.format("%.2f", price));
}
}密码强度验证
java
public class PasswordValidation {
public static void main(String[] args) {
// 验证密码强度:8位以上,包含大小写和数字
String password = "Abc12345";
// 方法1:单条正则
boolean strong = password.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$");
System.out.println("强密码: " + strong);
// 方法2:链式检查(更易读)
boolean check1 = password.matches(".*[a-z].*"); // 包含小写
boolean check2 = password.matches(".*[A-Z].*"); // 包含大写
boolean check3 = password.matches(".*\\d.*"); // 包含数字
boolean check4 = password.matches(".{8,}"); // 至少8位
boolean allPassed = check1 && check2 && check3 && check4;
System.out.println("全部通过: " + allPassed);
// 解释 (?=...) 零宽断言
// (?=.*[a-z]) 先看有没有小写,但不消耗字符
// (?=.*[A-Z]) 先看有没有大写,但不消耗字符
// (?=.*\\d) 先看有没有数字,但不消耗字符
// [a-zA-Z\\d]{8,} 最后才匹配8位以上的字母数字
}
}String 方法 vs Pattern/Matcher
什么时候用哪个?
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 验证格式 | String.matches() | 一行搞定,简单直观 |
| 替换子串 | String.replaceAll() | 无需创建 Matcher 对象 |
| 分割字符串 | String.split() | 性能比 Pattern.split() 好 |
| 找所有匹配 | Pattern + Matcher | 需要 find() 循环 |
| 分组捕获 | Pattern + Matcher | String 方法不支持 |
| 复杂替换逻辑 | Pattern + Matcher | 支持 lambda 函数式替换 |
总结
String 的正则方法适合简单场景:
- matches():快速验证,加
^和$确保完全匹配 - replaceAll():文本替换,支持反向引用
$1 - replaceFirst():只替换第一个
- split():字符串分割,注意尾部空串的处理
记住一个原则:简单场景用 String,复杂场景再上 Pattern/Matcher。
下一节是重磅内容:常用正则模板,收藏起来随时查。
