【问题标题】:Match a string against multiple regex patterns将字符串与多个正则表达式模式匹配
【发布时间】:2014-04-10 17:32:03
【问题描述】:

我有一个输入字符串。

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

Example Input: ABCD

我想匹配这些 reg-ex 模式,如果其中至少有一个匹配,则返回 true

[a-zA-Z]{3}

^[^\\d].*

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

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

【问题讨论】:

  • 你的问题不清楚(至少对我来说)。你说的有效是什么意思?您想检查输入是否与所有正则表达式匹配还是只有一个就足够了?您可以在输入预期输出旁边添加吗?
  • 我认为,首先,你应该用自然语言定义你想要匹配什么样的字符序列。您的示例不允许我们提取任何模式。或者,充其量,这是与您的输入最匹配的模式:“ABCD”。
  • 你想达到什么目的?
  • return rx1.matcher(s).matches() || rx2.matcher(s).matches() || ...;
  • @JasonC 具有讽刺意味的是,我也是唯一一个投票决定关闭的人 Unclear :)

标签: java regex


【解决方案1】:

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

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;

【讨论】:

  • 什么是最有效的方法:多个匹配器或通过管道连接的多个模式?
  • 或者你应该说*更有效率?
【解决方案2】:

您可以从单个正则表达式中创建一个大的正则表达式:

[a-zA-Z]{3}|^[^\\d].*|([\\w&&[^b]])*

【讨论】:

  • @MarkoTopolnik 感谢您的更正,不确定 | 的优先级所以为了安全起见,我把它包在括号里
  • 由于括号也意味着组捕获,因此应小心对待。
  • 但是有没有办法知道我的正则表达式中的哪一个实际匹配?我知道这可能不是@patan 的目标,但我需要类似的东西。
  • @Sap 您可以将每个表达式包装在括号中,然后使用 Matcher 的 group() 方法来确定哪个匹配,但您必须为每个组查询它。您最好使用单独的正则表达式和一堆 if else 语句。
【解决方案3】:

为避免重新创建 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();
}

【讨论】:

  • Matcher 存在的全部目的是每次创建它以维护单个匹配操作的状态。您的代码不是线程安全的。
  • @MarkoTopolnik 是的。感谢您指出了这一点。在一个线程环境中,重置 Matcher 似乎比重新创建它快一点,所以决定在我的回答中提及它。希望我的编辑能让我的答案更好。
【解决方案4】:

这里有一个替代方案。 请注意,这不会做的一件事是以特定顺序返回它们。但是可以通过例如按 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();
        }

    }   
}

【讨论】:

    【解决方案5】:

    就像 (Running multiple regex patterns on String) 中解释的那样,最好将每个正则表达式连接到一个大的正则表达式,而不是只运行一个匹配器。这是一个很大的改进,因为您经常重用正则表达式。

    【讨论】:

      【解决方案6】:

      我不确定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;
      }
      

      我不确定它会如何影响性能,但将它们与| 结合在一个正则表达式中也会有所帮助。

      【讨论】:

      • 这重新定义了逻辑或并且缺少括号。为什么不使用p1.matcher(s).matches() || p2.matcher(s).matches() || p3.matcher(s).matches()
      • 你说得对。我不知道 4 年前我在想什么。
      猜你喜欢
      • 2021-12-08
      • 1970-01-01
      • 1970-01-01
      • 2013-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-07
      相关资源
      最近更新 更多