如果没有 scalaz 和 shapeless,我认为 Scala 解析某些输入的理想方式是 Scala 解析器组合器。在你的例子中,我会尝试这样的事情:
import org.joda.time.DateTime
import scala.util.parsing.combinator.JavaTokenParsers
val input =
"""Event:005003:information:2013 12 06 12 37 55:n3.swmml20861:1:Full client swmml20861 registered [entry=280 PID=20864 queue=0x4ca9001b]
|RSET:m3node:AUTRS:1-1-24:A:0:LOADSHARE:INHIBITED:0
|M3UA_IP_LINK:m3node:AUT001LKSET1:AUT001LK1:r
|OPC:m3node:1-10-2(P):A7:NAT0""".stripMargin
trait LineContent
case class Event(number : Int, typ : String, when : DateTime, stuff : List[String]) extends LineContent
case class Reset(node : String, stuff : List[String]) extends LineContent
case class Other(typ : String, stuff : List[String]) extends LineContent
object LineContentParser extends JavaTokenParsers {
override val whiteSpace=""":""".r
val space="""\s+""".r
val lineEnd = """"\n""".r //"""\s*(\r?\n\r?)+""".r
val field = """[^:]*""".r
def stuff : Parser[List[String]] = rep(field)
def integer : Parser[Int] = log(wholeNumber ^^ {_.toInt})("integer")
def date : Parser[DateTime] = log((repsep(integer, space) filter (_.length == 6)) ^^ (l =>
new DateTime(l(0), l(1), l(2), l(3), l(4), l(5), 0)
))("date")
def event : Parser[Event] = "Event" ~> integer ~ field ~ date ~ stuff ^^ {
case number~typ~when~stuff => Event(number, typ, when, stuff)}
def reset : Parser[Reset] = "RSET" ~> field ~ stuff ^^ { case node~stuff =>
Reset(node, stuff)
}
def other : Parser[Other] = ("M3UA_IP_LINK" | "OPC") ~ stuff ^^ { case typ~stuff =>
Other(typ, stuff)
}
def line : Parser[LineContent] = event | reset | other
def lines = repsep(line, lineEnd)
def parseLines(s : String) = parseAll(lines, s)
}
LineContentParser.parseLines(input)
解析器组合器中的模式是不言自明的。我总是尽可能早地将每个成功解析的块转换为部分结果。然后将部分结果合并为最终结果。
调试提示:您始终可以添加log
解析器。它将在应用规则之前和之后打印。连同给定的名称(例如“日期”),它还将打印输入源的当前位置、应用规则的位置以及适用时解析的部分结果。
示例输出如下所示:
trying integer at scala.util.parsing.input.CharSequenceReader@108589b
integer --> [1.13] parsed: 5003
trying date at scala.util.parsing.input.CharSequenceReader@cec2e3
trying integer at scala.util.parsing.input.CharSequenceReader@cec2e3
integer --> [1.30] parsed: 2013
trying integer at scala.util.parsing.input.CharSequenceReader@14da3
integer --> [1.33] parsed: 12
trying integer at scala.util.parsing.input.CharSequenceReader@1902929
integer --> [1.36] parsed: 6
trying integer at scala.util.parsing.input.CharSequenceReader@17e4dce
integer --> [1.39] parsed: 12
trying integer at scala.util.parsing.input.CharSequenceReader@1747fd8
integer --> [1.42] parsed: 37
trying integer at scala.util.parsing.input.CharSequenceReader@1757f47
integer --> [1.45] parsed: 55
date --> [1.45] parsed: 2013-12-06T12:37:55.000+01:00
我认为这是一种将输入解析为类型良好的 Scala 对象的简单且可维护的方法。这一切都在核心 Scala API 中,因此我将其称为“惯用”。在 Idea Scala 工作表中键入示例代码时,完成和类型信息工作得非常好。因此,IDE 似乎很好地支持这种方式。