Skip to content

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 + MatcherString 方法不支持
复杂替换逻辑Pattern + Matcher支持 lambda 函数式替换

总结

String 的正则方法适合简单场景:

  1. matches():快速验证,加 ^$ 确保完全匹配
  2. replaceAll():文本替换,支持反向引用 $1
  3. replaceFirst():只替换第一个
  4. split():字符串分割,注意尾部空串的处理

记住一个原则:简单场景用 String,复杂场景再上 Pattern/Matcher

下一节是重磅内容:常用正则模板,收藏起来随时查。

基于 VitePress 构建