【发布时间】:2018-06-05 22:57:25
【问题描述】:
我想匹配包含在三重 "-quotes 中的字符串,这些字符串可能包含换行符,并且除了开头和结尾之外不包含任何 """-substrings。
有效示例:
"""foo
bar "baz" blah"""
无效示例:
"""foo bar """ baz"""
我尝试使用以下正则表达式(作为 Java String 文字):
"(?m)\"\"\"(?:[^\"]|(?:\"[^\"])|(?:\"\"[^\"]))*\"\"\""
它似乎适用于简短的例子。但是,在较长的示例中,例如在包含数千行 hello world 的字符串上,它会给我一个 StackOverflowError。
Scala sn-p 重现错误
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 sn-p
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 正则表达式 API 时不会产生 StackOverflowError?
我不在乎解决方案是使用 Scala 还是 Java(或其他),只要使用相同的底层 Java 正则表达式库即可。
【问题讨论】:
-
这里看起来确实有些用处:why
(A|B)*results in recursive calls。但是请注意,例如"(?m)\"\"\"(?:\"?\"?[^\"])*\"\"\""会遇到相同的错误,即使它不包含替代品|。 -
"""必须在字符串的开头和结尾吗?为什么不手动编写呢?它只需要多几行,而且速度会更快 -
@x4rf41 是的,三个双引号,后面跟着任何字符,只要没有三双引号,最后还是三个双引号。我希望有一个这样做的正则表达式,因为它很适合现有的框架。否则,我将不得不发明一些“通用的常规语言接受接口”——如果可能的话,这是我想要避免的。
-
... 也许我应该明确排除像
"""""""(七个双引号)这样的极端情况,表示带有单个双引号的字符串文字。假设字符串中的所有双引号都与开头的"""和结尾的"""用一些其他字符分隔。我不在乎那些奇怪的极端情况是被接受还是被拒绝。 -
试试这个
^"""((?!""")[\s\S])*"""$(没有多行标志)。它在这里工作:regex101.com 与您的两个示例都符合预期。它使用负面的前瞻性来代替。这应该可以防止我希望的堆栈溢出(虽然不确定)。转义:^\"\"\"((?!\"\"\")[\\s\\S])*\"\"\"$
标签: java regex scala stack-overflow