我正在尝试解析以下文档:
val doc = """BEGIN
A Bunch
Of Text
With linebreaks
##
"""
这里的想法是,当我在自己的一行上看到##
时,我应该考虑解析的结束。
我尝试过,使用以下代码解析此文档:
object MyParser extends RegexParsers {
val begin: Parser[String] = "BEGIN"
val lines: Parser[Seq[String]] = repsep(line, eol)
val line: Parser[String] = """.+""".r
val eol: Parser[Any] = "\n" | "\r\n" | "\r"
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end
}
MyParser.parseAll(MyParser.document, doc)
但是当我尝试执行此操作时(在Annonite脚本中),我得到以下内容:
java.lang.NullPointerException
scala.util.parsing.combinator.Parsers$class.rep1sep(Parsers.scala:771)
ammonite.$file.vtt$minusparser$MyParser$.rep1sep(vtt-parser.sc:3)
scala.util.parsing.combinator.Parsers$class.repsep(Parsers.scala:687)
ammonite.$file.vtt$minusparser$MyParser$.repsep(vtt-parser.sc:3)
ammonite.$file.vtt$minusparser$MyParser$.<init>(vtt-parser.sc:5)
ammonite.$file.vtt$minusparser$MyParser$.<clinit>(vtt-parser.sc)
ammonite.$file.vtt$minusparser$.<init>(vtt-parser.sc:22)
ammonite.$file.vtt$minusparser$.<clinit>(vtt-parser.sc)
谁能看到我出错的地方?
答案 0 :(得分:1)
错误的原因是line
和eol
被定义为普通类字段val
,但它们在定义之前在lines
中使用。将值分配给类字段的代码在构造函数中按顺序执行,line
和eol
在null
分配时仍为lines
。
要解决此问题,请将line
和eol
定义为lazy val
或def
s,或者将它们放在代码中的lines
之前。
解析器本身也存在一些问题。默认情况下,Scala解析器会自动忽略所有空格,包括EOL。考虑到没有任何标志的正则表达式.*
不包含EOL,line
自然意味着&#34;整行直到换行符#34;所以你不要#39 ; t必须分析EOL。
其次,定义的lines
解析器是贪婪的。它会愉快地消耗包括最终##
在内的所有内容。例如,要使其在end
之前停止,请使用not
组合器。
通过所有更改,解析器如下所示:
object MyParser extends RegexParsers {
val begin: Parser[String] = "BEGIN"
val line: Parser[String] = """.+""".r
val lines: Parser[Seq[String]] = rep(not(end) ~> line)
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end
}
您还可以覆盖跳过空格的行为,并手动分析所有空格。这包括BEGIN
之后和##
之后的空格:
object MyParser extends RegexParsers {
override def skipWhitespace = false
val eol: Parser[Any] = "\n" | "\r\n" | "\r"
val begin: Parser[String] = "BEGIN" <~ eol
val line: Parser[String] = """.*""".r
val lines: Parser[Seq[String]] = rep(not(end) ~> line <~ eol)
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end <~ whiteSpace
}
请注意,此处line
定义为.*
而不是.+
。像这样,如果输入中有任何空行,解析器就不会失败。