【问题标题】:Avoid overlapping regex matching in Java避免在 Java 中重叠正则表达式匹配
【发布时间】:2011-05-16 11:57:29
【问题描述】:

出于某种原因,这段 Java 代码给了我重叠匹配:

Pattern pat = Pattern.compile("(" + leftContext + ")" + ".*" + "(" + rightContext + ")", Pattern.DOTALL);

任何方式/选项,以避免检测重叠?例如 leftContext rightContext rightContext 应该是 1 匹配而不是 2

这是完整的代码:

public static String replaceWithContext(String input, String leftContext, String rightContext, String newString){   
  Pattern pat = Pattern.compile("(" + leftContext + ")" + ".*" + "(" + rightContext + ")", Pattern.DOTALL);
  Matcher matcher = pat.matcher(input);
  StringBuffer buffer = new StringBuffer();

  while (matcher.find()) { 
   matcher.appendReplacement(buffer, "");
   buffer.append(matcher.group(1) + newString + matcher.group(2));
  }
  matcher.appendTail(buffer);

  return buffer.toString();
 }

所以这是使用否定前瞻的最终答案,我没有意识到 * 是贪婪的:

Pattern pat = Pattern.compile("(" +
    leftContext + ")" + "(?:(?!" +
    rightContext + ").)*" + "(" +
    rightContext + ")", Pattern.DOTALL);

【问题讨论】:

  • 你能告诉我们leftContext和rightContext是什么吗?并给我们一个失败匹配的例子。
  • 让正则表达式匹配器捕获重叠的东西通常是一件有点棘手的事情,而不是默认情况下发生的事情。没有看到模式的内容,很难说发生了什么。它基本上需要环顾四周才能让匹配器不止一次地遍历字符串的同一部分。你这样做吗?
  • rightContext 和 leftContext 是纯字符串,例如 leftContext="ab" rightContext="cd"
  • * 量词默认是贪婪的,你描述的正则表达式不会产生多个匹配。你为什么不发布一个完整的例子?
  • 啊,这就是发生的事情,我有没有机会将 * 的默认行为更改为非贪婪?

标签: java regex matcher


【解决方案1】:

您对“重叠”一词的使用令人困惑。显然,您的意思是正则表达式太贪心,匹配从第一个 leftContext 到最后一个 rightContext 的所有内容。看来您已经想到了这一点——并且还提出了一种更好的方法——但至少仍然存在一个潜在问题。

您说leftContextrightContext 是“普通字符串”,我假设您的意思是它们不应该被解释为正则表达式,但它们会。您需要对它们进行转义,否则它们包含的任何正则表达式元字符都会导致不正确的结果或运行时异常。您的替换字符串也是如此,尽管只有 $ 和反斜杠在那里有特殊含义。这是一个例子(也请注意非贪婪的.*?):

public static String replaceWithContext(String input, String leftContext, String rightContext, String newString){
  String lcRegex = Pattern.quote(leftContext);
  String rcRegex = Pattern.quote(rightContext);
  String replace = Matcher.quoteReplacment(newString);
  Pattern pat = Pattern.compile("(" + lcRegex + ").*?(" + rcRegex + ")", Pattern.DOTALL);

另一件事:如果您不对匹配的文本进行任何匹配后处理,则可以使用replaceAll 而不是使用appendReplacementappendTail 滚动您自己的:

return input.replaceAll("(?s)(" + lcRegex + ")" +
                        "(?:(?!" + rcRegex + ").)*" +
                        "(" + rcRegex + ")",
    "$1" + replace + "$2");

【讨论】:

    【解决方案2】:

    可能性很小,具体取决于您的实际需求。

    您可以在正则表达式的末尾附加$,如下所示:

    "(" + leftContext + ")" + ".*" + "(" + rightContext + ")$"
    

    所以如果rightContext 不是最后一件事,您的正则表达式将不匹配。

    接下来,你可以捕获rightContext之后的所有内容:

    "(" + leftContext + ")" + ".*" + "(" + rightContext + ")(.*)"
    

    然后丢弃第三个匹配组中的所有内容。

    但是,由于我们不知道 leftContextrightContext 到底是什么,也许你的问题出在它们之内。

    【讨论】:

    • mm,不确定在我的代码中如何工作,我不能只丢弃部分输入字符串
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-24
    • 2017-11-10
    相关资源
    最近更新 更多