2. 特殊正则表达式字符

Java正则表达式API(java.util.regex)中的元字符具有特殊含义。当需要将它们视为普通字符时,必须进行转义。常见需要转义的元字符包括:

<([{\^-=$!|]})?*+.>

⚠️ 这些元字符在正则中有特殊功能,直接使用会导致意外匹配。比如点号.实际匹配任意字符,而非字面量点号。看这个踩坑示例:

@Test
public void givenRegexWithDot_whenMatchingStr_thenMatches() {
    String strInput = "foof";
    String strRegex = "foo.";
      
    assertEquals(true, strInput.matches(strRegex)); // 实际匹配到"foof"而非"foo."
}

这里正则foo.匹配了"foof"——因为点号匹配了任意字符f。如果我们想精确匹配字面量点号,就需要转义。

3. 字符转义方法

Java提供两种主流转义方式:

3.1. 使用反斜杠转义

在元字符前加反斜杠\。但注意在Java字符串中,反斜杠本身需要转义,因此要写成\\

@Test
public void givenRegexWithDotEsc_whenMatchingStr_thenNotMatching() {
    String strInput = "foof";
    String strRegex = "foo\\."; // 转义后的点号

    assertEquals(false, strInput.matches(strRegex)); // 现在只匹配"foo."而非"foof"
}

✅ 这种方式简单直接,适合转义单个字符。

3.2. 使用\Q和\E标记

\Q\E包裹需要转义的字符序列,中间所有元字符都会被自动转义:

@Test
public void givenRegexWithPipeEscaped_whenSplitStr_thenSplits() {
    String strInput = "foo|bar|hello|world";
    String strRegex = "\\Q|\\E"; // 转义管道符|
    
    assertEquals(4, strInput.split(strRegex).length); // 成功按|分割字符串
}

✅ 适合转义包含多个元字符的复杂模式。

4. Pattern.quote() 方法

Pattern.quote(String)是更优雅的转义方案,它自动在字符串前后添加\Q\E

@Test
public void givenRegexWithPipeEscQuoteMeth_whenSplitStr_thenSplits() {
    String strInput = "foo|bar|hello|world";
    String strRegex = "|"; // 原始管道符

    assertEquals(4, strInput.split(Pattern.quote(strRegex)).length); // 自动转义
}

⚠️ 注意:quote()会转义整个字符串。如果需要单独转义特定字符,仍需使用手动转义或替换算法。

5. 转义字符串中的所有特殊字符

当需要批量转义字符串中所有元字符时,可以遍历字符处理:

public void givenStringWithSpecialCharacters_whenUsingCharacterClass_thenReplace() {
    String inputString = "#$%^&*() SimpleText123 ";
    StringBuilder escapedString = new StringBuilder();
    for (char c : inputString.toCharArray()) {
        if (!Character.isLetterOrDigit(c)) {
            escapedString.append("\\"); // 非字母数字前加反斜杠
        }
        escapedString.append(c);
    }
    String expectedOutputString = "\\#\\$\\%\\^\\&\\*\\(\\)\\ SimpleText123\\ ";
    assertEquals(expectedOutputString, escapedString.toString());
}

✅ 这种方式适合动态处理用户输入等场景。

6. 更多实战案例

6.1. 替换操作中的转义

使用replaceAll()时,忘记转义元字符会导致意外结果。比如替换美元符号$

@Test
public void givenRegexWithDollar_whenReplacing_thenNotReplace() {
    String strInput = "I gave $50 to my brother."
      + "He bought candy for $35. Now he has $15 left.";
    String strRegex = "$"; // 未转义
    String strReplacement = "£";
    
    Pattern p = Pattern.compile(strRegex);
    Matcher m = p.matcher(strInput);
        
    assertThat("替换失败", not(equalTo(m.replaceAll(strReplacement)))); // 断言失败
}

❌ 因为$在正则中表示行尾,直接使用无法匹配字面量$

正确做法是转义$

@Test
public void givenRegexWithDollarEsc_whenReplacing_thenReplace() {
    String strInput = "I gave $50 to my brother."
      + "He bought candy for $35. Now he has $15 left.";
    String strRegex = "\\$"; // 转义后
    String strReplacement = "£";
    
    Pattern p = Pattern.compile(strRegex);
    Matcher m = p.matcher(strInput);
    
    String output = "I gave £50 to my brother."
      + "He bought candy for £35. Now he has £15 left.";
    assertEquals(output, m.replaceAll(strReplacement)); // 替换成功
}

✅ 转义后$被正确识别为字面量字符。

7. 总结

掌握正则转义是Java开发者的必备技能:

  1. 核心元字符<([{\^-=$!|]})?*+.> 需要特殊处理
  2. 三种转义方式
    • 反斜杠\\:适合单个字符
    • \Q...\E:适合字符序列
    • Pattern.quote():最简洁的自动转义
  3. 批量转义:通过字符遍历实现
  4. 常见坑点:替换操作中忘记转义元字符

本文所有代码示例可在GitHub仓库中查看。