我想匹配用三重"-引号引起来的字符串,这些字符串可能包含换行符,并且除了开头和结尾之外都不包含任何"""-子字符串。

有效的例子:

"""foo
bar "baz" blah"""


无效的示例:

"""foo bar """ baz"""


我尝试使用以下正则表达式(作为Java String文字):

"(?m)\"\"\"(?:[^\"]|(?:\"[^\"])|(?:\"\"[^\"]))*\"\"\""


它似乎可以在一些简短的例子中发挥作用。但是,在较长的示例中,例如在由hello world组成的包含数千行的字符串上,它给了我StackOverflowError

Scala代码段重现错误

import java.util.regex.{Pattern, Matcher}

val text = "\"" * 3 + "hello world \n" * 1000 + "\"" * 3
val p = Pattern.compile("(?m)\"\"\"(?:[^\"]|(?:\"[^\"])|(?:\"\"[^\"]))*\"\"\"")
println(p.matcher("\"\"\" foo bar baz \n baz bar foo \"\"\"").lookingAt())
println(p.matcher(text).lookingAt())


(注意:本地测试,Scastie超时;或者将1000减少到较小的数字?)。

产生相同错误的Java代码段

import java.util.regex.Pattern;
import java.util.regex.Matcher;

class RegexOverflowMain {
  public static void main(String[] args) {
    StringBuilder bldr = new StringBuilder();
    bldr.append("\"\"\"");
    for (int i = 0; i < 1000; i++) {
      bldr.append("hello world \n");
    }
    bldr.append("\"\"\"");
    String text = bldr.toString();
    Pattern p = Pattern.compile("(?m)\"\"\"(?:[^\"]|(?:\"[^\"])|(?:\"\"[^\"]))*\"\"\"");
    System.out.println(p.matcher("\"\"\" foo bar baz \n baz bar foo \"\"\"").lookingAt());
    System.out.println(p.matcher(text).lookingAt());
  }
}




知道如何使此“堆栈安全”的想法,即有人可以找到接受相同语言但在馈入Java regex API时不产生StackOverflowError的正则表达式吗?

我不在乎解决方案是在Scala还是Java(或其他)中,只要使用相同的基础Java regex库即可。

最佳答案

解决方案:使用否定提前查找来基本上找到以"""开头和"""结束并且包含不包含"""内容的字符串

作为纯正则表达式:^"""((?!""")[\s\S])*"""$

由于Java转义了正则表达式:"^\"\"\"((?!\"\"\")[\\s\\S])*\"\"\"$

\s\S包括换行符(基本上是. +换行符或带有单个行标志的.

应该在没有多行标志的情况下使用此命令,以便^$匹配字符串的开头和结尾,而不是行的开头和结尾。

否则这:

""" ab"""abc"""abc """

会匹配

我也以此为参考来排除"""Regular expression to match a line that doesn't contain a word?

09-11 19:14
查看更多