我希望匹配三"
个括号中的字符串 - 可能包含换行符的引号,并且除了最开头和最后一行之外不包含任何"""
- 子字符串。< / p>
有效示例:
"""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正则表达式API时不会产生StackOverflowError
吗?
我不关心解决方案是使用Scala还是Java(或其他),只要使用相同的底层Java正则表达式库。
答案 0 :(得分:2)
使用否定前瞻的解决方案基本上找到以"""
开头并以"""
结尾的字符串,其中包含不包含"""
的内容
作为普通正则表达式:^"""((?!""")[\s\S])*"""$
随着Java转义正则表达式:"^\"\"\"((?!\"\"\")[\\s\\S])*\"\"\"$
&#34;
\s\S
包含换行符(基本上.
+换行符或.
带有单行标记)
这应该在没有多行标志的情况下使用,以便^
和$
匹配字符串的开头和结尾,而不是行的开头和结尾
否则:
""" ab
"""abc"""
abc """
会匹配
我也将此作为参考,以排除"""
:Regular expression to match a line that doesn't contain a word?
答案 1 :(得分:2)
下面的完整答案优化了正则表达式的性能,但为了防止堆栈溢出,作为一个简单的解决方案,只需使重复组占有。
具有选择权的非占有性重复组需要递归调用以允许回溯。使其具有占有性可以解决问题,因此只需在+
之后添加*
:
"(?m)\"\"\"(?:[^\"]|(?:\"[^\"])|(?:\"\"[^\"]))*+\"\"\""
另请注意,如果您要匹配整个输入,则需要拨打matches()
,而不是lookingAt()
。
提升绩效
注意:快速性能测试表明,这比answer by x4rf41中的正则表达式强<<>> <强> 6倍
而不是匹配其中一个
[^\"]
(?:\"[^\"])
(?:\"\"[^\"])
在一个循环中,首先将所有内容与引用匹配。如果是单引号或双引号,而不是三引号,则匹配1-2引号,然后根据需要重复所有内容。最后匹配结束的三重引用。
这种匹配是确定的,所以使重复占有欲。如果输入有许多嵌入式引号,这也可以防止堆栈溢出。
"{3} match 3 leading quotes
[^"]*+ match as many non-quotes as possible (if any) {possesive}
(?: start optional repeating group
"{1,2} match 1-2 quotes
[^"]++ match one or more non-quotes (at least one) {possesive}
)*+ end optional repeating group {possesive}
"{3} match 3 trailing quotes
由于您不使用^
或$
,因此无需(?m)
(MULTILINE)
作为Java字符串:
"\"{3}[^\"]*+(?:\"{1,2}[^\"]++)*+\"{3}"