使用解析器组合整理文本行(Using parser combinators to collate l

2019-09-18 06:42发布

我试图解析使用解析器组合的文本文件。 我想捕获在一个名为分类的索引和文本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正则表达式,没有做多行。 我怎样才能改变这种做法,它正确地解析?

Answer 1:

  • 什么是错误讯息是什么意思?

根据你的语法定义, ".*".r <~ (divider | "END")你告诉的是,一个解析器example应该遵循或者由dividerEND 。 解析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])意味着它是一个正超前和将匹配随后由字符\r\n

解析结果是:

[10.1]解析:列表(实施例(0,列表(blah1,blah2,blah3)),实施例(1,列表(blah4,blah5)))

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



Answer 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
))


Answer 3:

我想这个问题可能是与“*”:R正则表达式,没有做多行。

究竟。 使用DOTALL修饰符(奇怪的是被称为“S”):

def example:  Parser[String]        = "(?s).*".r <~ (divider | "END") 


文章来源: Using parser combinators to collate lines of text