首页 > 解决方案 > 字符类中的元字符 (`[]`)

问题描述

我对 Java Regex 的理解是这样的。有两组元字符:

如果我们希望某个字符(例如连字符-)在字符类中按字面意思匹配,我们必须用反斜杠 ( \) 对其进行转义。

Java Doc中的以下描述支持此视图

字符类 字符类可能出现在其他字符类中,并且可能由联合运算符(隐式)和交集运算符(&&)组成。联合运算符表示一个类,该类包含至少一个操作数类中的每个字符。交集运算符表示一个类,该类包含其两个操作数类中的每个字符。

字符类运算符的优先级如下,从高到低:

  1. 文字转义 \x
  2. 分组 [...]
  3. 范围
  4. 联合 [ae][iu]
  5. 交点 [az&&[aeiou]]

请注意,字符类内部的元字符集与字符类外部的元字符集不同。

这是一个正确的理解吗?

令我吃惊的是,要转义字符类中的第二组,除了使用反斜杠之外,我们似乎还可以使用java.util.regex.Pattern.quote(). 我认为该方法仅适用于第一组元字符。

测试程序

以下测试程序说明Pattern.quote()and \(以及\Qand \E)都可用于在字符类中引用连字符:

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;

import java.util.regex.Pattern;

public class RegexTest {
    @Rule
    public TestName testName = new TestName();

    @Test
    public void testHyphenCharClassByPatternQuote() {
        String regex = "[" + Pattern.quote("-") + "]";
        Pattern p = Pattern.compile(regex);
        String[] tests = {
            "-"
            , "a"
        };
        for (String test : tests) {
            System.out.println(testName.getMethodName() + " matching " + test + ":" + p.matcher(test).matches());
        }
    }

    @Test
    public void testHyphenCharClassByBackSlash() {
        String regex = "[\\-]";
        Pattern p = Pattern.compile(regex);
        String[] tests = {
            "-"
            , "a"
        };
        for (String test : tests) {
            System.out.println(testName.getMethodName() + " matching " + test + ":" + p.matcher(test).matches());
        }
    }

    @Test
    public void testHyphenCharClassByQE() {
        String regex = "[\\Q-\\E]";
        Pattern p = Pattern.compile(regex);
        String[] tests = {
            "-"
            , "a"
        };
        for (String test : tests) {
            System.out.println(testName.getMethodName() + " matching " + test + ":" + p.matcher(test).matches());
        }
    }

}

测试输出

testHyphenCharClassByQE matching -:true
testHyphenCharClassByQE matching a:false
testHyphenCharClassByBackSlash matching -:true
testHyphenCharClassByBackSlash matching a:false
testHyphenCharClassByPatternQuote matching -:true
testHyphenCharClassByPatternQuote matching a:false

标签: javaregexpattern-matching

解决方案


您基本上是正确的,但是该pattern.quote()方法按预期工作。似乎让您感到困惑的是字符类别范围内连字符的性质。

Pattern 类文档在引用 部分下列出了以下转义修饰符:

\ 无,但引用以下字符
\Q无,但引用所有字符,直到\E
\E无,但结束引用开始于\Q

pattern.quote所做的就是用文字包装输入并\Q生成\E文字字符串。

引用 Java 文档quote

返回指定字符串的文字模式字符串。此方法生成一个字符串,可用于创建一个模式,该模式将匹配字符串 s,就好像它是文字模式一样。
输入序列中的元字符或转义序列将没有特殊含义。

从技术上讲,从 a \Q(exclusive) 到 next \E(exclusive) 的所有内容,可能包括任意数量的\Q序列,都是字面化的。

当您在字符类中转义/引用连字符(或将其放在末尾)时,它将失去其特殊含义,即定义一个范围,并且它只是变成一个文字连字符,如您自己所证明的:

String regex = "[a\\-z]";
Pattern p = Pattern.compile(regex);
String[] tests = {
    "-"
    , "a"
    , "b"
    , "z"
};
for (String test : tests) {
    System.out.println(" matching " + test + ":" + p.matcher(test).matches());
}

输出:

 matching -:true
 matching a:true
 matching b:false
 matching z:true

推荐阅读