我正在尝试使用解析器组合器解析文本文件。我想在名为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
正则表达式不做多行。如何更改它以便正确解析?
答案 0 :(得分:5)
根据您的语法定义".*".r <~ (divider | "END")
,您告诉解析器,example
应遵循divider
或END
。在解析blah1之后,解析器尝试找到divider
并失败,然后尝试END
,再次失败,没有其他选项可用,所以END
这里是最后一个选项生产价值,所以从解析者的角度来看,它预期END
,但很快发现,下一个输入是第4行的blah2
。
尝试接近您的实现,您的案例中的语法应为:
examples ::= {divider example}
divider ::= Integer")"
example ::= {literal ["END"]}
我认为将“example”解析为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)))
如果要将其解析为String(而不是List[String]
),只需添加转换函数,例如:^^ {_ mkString "\n"}
答案 1 :(得分: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
))
答案 2 :(得分:1)
我认为问题可能出在&#34;。*&#34; .r正则表达式没有做 多行。
完全。使用dotall修饰符(奇怪地称为&#34; s&#34;):
def example: Parser[String] = "(?s).*".r <~ (divider | "END")