匹配一个字符串与多个正则表达式模式

28

我有一个输入字符串。

我在思考如何有效地将此字符串与多个正则表达式匹配。

Example Input: ABCD

我想针对这些正则表达式进行匹配,并在其中至少有一个匹配时返回true

[a-zA-Z]{3}

^[^\\d].*

([\\w&&[^b]])*

我不确定如何一次匹配多个模式。有人能告诉我我们如何有效地做到这一点吗?


4
你的问题不够明确(至少对我来说是这样)。你所说的“有效地”是什么意思?你想检查输入是否与所有正则表达式匹配,还是只需要一个就足够了?除了输入,你能否提供预期输出的其他信息? - Pshemo
1
我认为首先你应该用自然语言定义你想匹配的字符序列。你的例子并没有让我们提取出任何模式。或者,最好的情况是,这里有一个最能匹配你输入的模式:"ABCD"。 - Andrei Nicusan
你想要实现什么目标? - wumpz
3
return rx1.matcher(s).matches() || rx2.matcher(s).matches() || ...; 可以翻译为:如果字符串 s 匹配正则表达式 rx1rx2 或其他正则表达式,则返回 true。 - Marko Topolnik
1
讽刺的是,我也是唯一一个投票将其关闭为不明确的人 :) - Marko Topolnik
显示剩余6条评论
6个回答

36

如果你只有几个正则表达式,并且它们在编译时都已知,那么这就足够了:

private static final Pattern
  rx1 = Pattern.compile("..."),
  rx2 = Pattern.compile("..."),
  ...;

return rx1.matcher(s).matches() || rx2.matcher(s).matches() || ...;

如果有更多的规则,或者它们是在运行时加载的,那么请使用一系列模式:

final List<Pattern> rxs = new ArrayList<>();


for (Pattern rx : rxs) if (rx.matcher(input).matches()) return true;
return false;

14
哪种方式更有效:多个匹配器还是由管道连接的多个模式? - Benj
1
或者你应该说“更高效”? - Kiran

30
你可以将单个的正则表达式合并成一个大的正则表达式:
[a-zA-Z]{3}|^[^\\d].*|([\\w&&[^b]])*

2
@MarkoTopolnik 感谢您的更正,我对 | 的优先级不确定,所以将其用括号括起来以确保安全。 - vandale
3
由于括号也暗示了分组捕获,因此应谨慎处理。 - Marko Topolnik
但是,有没有办法知道我的正则表达式中哪个实际匹配了?我明白这可能不是@patan的目标,但我需要类似的东西。 - Sap
@Sap,你可以将每个表达式都用括号括起来,然后使用Matcher的group()方法确定哪个匹配,但是你必须为每个组查询它。最好使用单独的正则表达式和一堆if else语句。 - vandale

2

就像在(在字符串上运行多个正则表达式模式)中所解释的那样,最好将每个正则表达式连接成一个大的正则表达式,然后仅运行匹配器一次。如果您经常重复使用正则表达式,则这是一项很大的改进。


1
这里有一个替代方案。 请注意,这个方案没有按特定顺序返回结果。但是可以通过按照 m.start() 进行排序来实现。
private static HashMap<String, String> regs = new HashMap<String, String>();

...

    regs.put("COMMA", ",");
    regs.put("ID", "[a-z][a-zA-Z0-9]*");
    regs.put("SEMI", ";");
    regs.put("GETS", ":=");
    regs.put("DOT", "\\.");

    for (HashMap.Entry<String, String> entry : regs.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        Matcher m = Pattern.compile(value).matcher("program var a, b, c; begin a := 0; end.");
        boolean f = m.find();
        while(f) 
        {
            System.out.println(key);
            System.out.print(m.group() + " ");
            System.out.print(m.start() + " ");
            System.out.println(m.end());
            f = m.find();
        }

    }   
}

1
为避免重复创建Pattern和Matcher类的实例,您可以创建一个每个并重复使用它们。要重用Matcher类,您可以使用reset(newInput)方法。 警告:此方法不是线程安全的。仅在可以保证只有一个线程能够使用此方法时使用它,否则为每个方法调用创建单独的Matcher实例。 这是可能的代码示例之一。
private static Matcher m1 = Pattern.compile("regex1").matcher("");
private static Matcher m2 = Pattern.compile("regex2").matcher("");
private static Matcher m3 = Pattern.compile("regex3").matcher("");

public boolean matchesAtLeastOneRegex(String input) {
    return     m1.reset(input).matches() 
            || m2.reset(input).matches()
            || m3.reset(input).matches();
}

3
Matcher 的存在目的是每次创建它以维护单个匹配操作的状态。你的代码不是线程安全的。 - Marko Topolnik
@MarkoTopolnik 确实。感谢您指出这一点。在单线程环境中,重置Matcher似乎比重新创建它要快一些,因此我决定在我的答案中提到它。希望我的编辑能使我的答案更好。 - Pshemo

0

我不确定 effectively 是什么意思,但如果它涉及性能并且您想检查很多字符串,我会选择这个。

...
static Pattern p1 = Pattern.compile("[a-zA-Z]{3}");
static Pattern p2 = Pattern.compile("^[^\\d].*");
static Pattern p3 = Pattern.compile("([\\w&&[^b]])*");

public static boolean test(String s){
   return p1.matcher(s).matches ? true: 
        p2.matcher(s).matches ? true: 
        p3.matcher(s).matches;
}

我不确定它会如何影响性能,但是使用|将它们全部组合在一个正则表达式中也可能有所帮助。


6
这段代码重新定义了逻辑或操作符,并且缺少括号。为什么不使用 p1.matcher(s).matches() || p2.matcher(s).matches() || p3.matcher(s).matches() 呢? - kratenko
3
你说得完全正确。我不知道4年前当时我在想什么。 - NeplatnyUdaj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接