正则表达式匹配Java字符串

8
在Scala的解析器组合器中(特别是JavaTokensParser),有一个名为stringLiteral的定义,用于匹配类似Java的字符串。
def stringLiteral: Parser[String] =
              ("\""+"""([^"\p{Cntrl}\\]|\\[\\'"bfnrt]|\\u[a-fA-F0-9]{4})*"""+"\"").r

不幸的是,这个正则表达式 对于长字符串不起作用。 有人知道一个可重复使用的实现吗?或者有一个更节省空间的正则表达式修改方法吗?

1个回答

3
Interesting problem!!
刚刚尝试了一下,得出了以下结果:
val r = ("\"" + "(?:[^\"\\p{Cntrl}\\\\]*|(?:\\\\(?:[\\\\'\"bfnrt]|u[a-fA-F0-9]{4}))*)*" + "\"").r

注意:上面的正则表达式已经修复,需要重复前导'\'和尾随字符,而不仅仅是尾随字符,就像我最初写的那样!
编辑:发现了一种更高效的正则表达式。使用以下正则表达式,它可以解析长达950个\\ns对的字符串,而原始版本只能解析556个,在我的默认配置中至少如此。
val r = ("\"" + "(?:[^\"\\p{Cntrl}\\\\]*|\\\\[\\\\'\"bfnrt]|\\\\u[a-fA-F0-9]{4})*" + "\"").r

编辑 2:根据@schmmd的评论,我有一个更好的正则表达式。这个可以解析出2500个\ns的情况。秘密是使用贪婪占有模式,这基本上关闭了回溯的需求,因此也关闭了递归。

val r = (""""([^"\p{Cntrl}\\]*+(?:\\[\\'"bfnrt])*+(?:\\u[a-fA-F0-9]{4})*+)*+"""").r

解决方案的核心是尝试每次匹配尽可能多的内容。
scala> val r = (""""([^"\p{Cntrl}\\]*+(?:\\[\\'"bfnrt])*+(?:\\u[a-fA-F0-9]{4})*+)*+"""").r
r: scala.util.matching.Regex = "([^"\p{Cntrl}\\]*+(?:\\[\\'"bfnrt])*+(?:\\u[a-fA-F0-9]{4})*+)*+"

scala> r.pattern.matcher("\"" + "\\ns" * 2500 + "\"").lookingAt
res4: Boolean = true

scala> r.pattern.matcher("\"" + "s" * 2500 + "\"").lookingAt
res5: Boolean = true

更新: 一个拉取请求已提交给Scala开发人员。并且被接受了。


你的正则表达式很好,但是我认为删除所有的逻辑分支符号会更好。不过,当输入 "\\ns" * 2500 时,仍然会溢出。("\"" + """([^"\p{Cntrl}\\]*(?:\\[\\'"bfnrt])*(?:\\u[a-fA-F0-9]{4})*)*""" + "\"").r. - schmmd
将所有的Kleene星号转换为具有占有特性的Kleene星号可以提高性能。(""" + """([^"\p{Cntrl}\]+(?:\[\'"bfnrt])+(?:\u[a-fA-F0-9]{4})+)+""" + """).r - schmmd
@schmmd 你是正确的!'possessive operator' 确实是答案。你甚至可以在原来的回答中使用它,这也解决了问题。 - Paul Wagland
我现在接受你的答案。我很想知道是否有一个库可以解析这个模式,并提供将解析后的Java字符串转换为“String”实例的方法。 - schmmd
1
我希望这能被提交到Scala库中。 - Daniel C. Sobral

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接