2

我正在尝试使用解析器组合器解析文本文件。我想在一个名为Example. 这是一个显示输入文件表单的测试:

object Test extends ParsComb with App {
  val input = """
0)
blah1
blah2
blah3
1)
blah4
blah5
END
"""
  println(parseAll(examples, input))
}

这是我的尝试不起作用:

import scala.util.parsing.combinator.RegexParsers

case class Example(index: Int, text: String)

class ParsComb extends RegexParsers {
  def examples: Parser[List[Example]] = rep(divider~example) ^^ 
                                          {_ map {case d ~ e => Example(d,e)}}
  def divider:  Parser[Int]           = "[0-9]+".r <~ ")"    ^^ (_.toInt)
  def example:  Parser[String]        = ".*".r <~ (divider | "END") 
}

它失败了:

[4.1] failure: `END' expected but `b' found

blah2

^

我只是从这些开始,所以我不太清楚我在做什么。我认为问题可能与".*".r正则表达式不做多行有关。如何更改它以使其正确解析?

4

3 回答 3

5
  • 错误信息是什么意思?

根据您的语法定义,".*".r <~ (divider | "END")您告诉解析器 anexample后面应该跟 adivider或 a END。解析 blah1 后,解析器尝试查找divider并失败,然后尝试END,再次失败,没有其他可用选项,所以END这里是生产值的最后一个替代方案,所以从解析器的角度来看,它是预期的END,但很快发现,下一个输入blah2来自第 4 行。

  • 如何解决?

尽量接近你的实现,你的情况下的语法应该是:

examples ::= {divider example}
divider  ::= Integer")"
example  ::= {literal ["END"]}

而且我认为将“示例”解析为List[String]更有意义,无论如何,这取决于您。

问题是你的example解析器,它应该是一个可重复的文字。

所以 ,

class ParsComb extends RegexParsers {
  def examples: Parser[List[Example]] = rep(divider ~ example) ^^ { _ map { case d ~ e => Example(d, e) } }
  def divider: Parser[Int] = "[0-9]+".r <~ ")" ^^ (_.toInt)
  def example: Parser[List[String]] = rep("[\\w]*(?=[\\r\\n])".r <~ opt("END"))
}

正则表达式(?=[\\r\\n])意味着它是一个积极的前瞻,并且会匹配后面跟着的字符\ror \n

解析结果是:

[10.1]解析:List(Example(0,List(blah1,blah2,blah3)),Example(1,List(blah4,blah5)))

如果要将其解析为字符串(而不是List[String]),只需添加一个转换函数,例如:^^ {_ mkString "\n"}

于 2012-06-20T10:46:07.550 回答
2

您的解析器无法处理换行符,您的example解析器消除了下一个divider并且您的example正则表达式匹配divider和“END”字符串。

尝试这个:

object ParsComb extends RegexParsers { 
  def examples: Parser[List[Example]] = rep(divider~example) <~ """END\n?""".r ^^ {_ map {case d ~ e => Example(d,e)}} 
  def divider: Parser[Int] = "[0-9]+".r <~ ")\n" ^^ (_.toInt) 
  def example: Parser[String] = rep(str) ^^ {_.mkString}
  def str: Parser[String] = """.*\n""".r ^? { case s if simpleLine(s) => s}

  val div = """[0-9]+\)\n""".r
  def simpleLine(s: String) = s match {
    case div() => false
    case "END\n" => false
    case _ => true
  }

  def apply(s: String) = parseAll(examples, s)
}

结果:

scala> ParsComb(input)
res3: ParsComb.ParseResult[List[Example]] =
[10.1] parsed: List(Example(0,blah1
blah2
blah3
), Example(1,blah4
blah5
))
于 2012-06-20T06:18:08.967 回答
1

我认为问题可能出在 ".*".r 正则表达式没有做多行。

确切地。使用 dotall 修饰符(奇怪地称为“s”):

def example:  Parser[String]        = "(?s).*".r <~ (divider | "END") 
于 2012-06-20T17:07:45.940 回答