我正在尝试实现ISO 8601日期/时间格式解析器,并在可选的时间部分遇到一些麻烦。我构建了一个简单的问题示例:
class ISO8601 extends RegexParsers {
val hour = s"[0-9]{2}".r ^^ {_.toInt}
val minute = s"[0-9]{2}".r ^^ {_.toInt}
val timeSep = ":"
val test = (hour ~ opt(timeSep ~> minute) |
hour ~ opt(minute)) ^^ {
case hh ~ mmOpt =>
val mm = mmOpt.getOrElse(0)
(hh, mm, 0, 0)
}
}
我想要做的是允许以下时间格式:
我的解析器成功解析“23”和“23:30”,但拒绝解析“2330”:
isoRes: iso.ParseResult[(Int, Int, Int, Int)] = [1.3] failure: string matching regex `\z' expected but `3' found
2330
不应解析该失败的解析器并尝试匹配第二个选项(在“|”之后)?
答案 0 :(得分:2)
问题是opt()
解析器。首先,我假设你这样称呼它:
parseAll(ISO8601.test, new CharSequenceReader("2330"))
那会发生什么? parseAll
将尝试解析阅读器中的所有输入,即直到它不再返回字符为止。
因此使用test
解析器,它尝试第一个替代方案并解析" 23"。然后没有分隔符,因此opt()
解析器将返回None
并且第一个替代成功。所以没有必要检查第二种选择。然后读者中仍然存在字符3
和0
,但解析器应该在输入的末尾!这就是你失败的原因。
现在尝试:
println(ISO8601.parseAll(ISO8601.rep(ISO8601.test), new CharSequenceReader("2330")))
输出:
[1.5] parsed: List((23,0,0,0), (30,0,0,0))
所以你看到第一个替代品被使用过2次。
那你怎么解决呢?另一种方法是使分钟可选,分钟中的分隔符也可选。
def test = hour ~ opt(opt(timeSep) ~> minute) map {
case h ~ None => (h, 0, 0, 0)
case h ~ Some(mm) => (h, mm, 0, 0)
}
使用" 23"," 2330"," 23:30"连续运行,你得到:
[1.3] parsed: (23,0,0,0)
[1.6] parsed: (23,30,0,0)
[1.5] parsed: (23,30,0,0)
顺便说一下,您应该在hour
和minute
解析器中添加一些检查,否则"9999"
是有效输入。
答案 1 :(得分:0)
我这样做:
val time = "^([0-9]{2}):?([0-9]{0,2})$".r
def parse(str: String) = str match {
case time(h, m) => (h, if (m == "") 0 else m, 0, 0)
}
parse("12")
parse("13:21")
parse("1456")
答案 2 :(得分:0)
嗯,我想我已经弄明白了。
原因是第一个替代匹配仅消费" 23", 所以测试术语是" 23"其余的输入是" 30"。 然后解析器期望输入结束但看到剩余的" 30"。
class ISO8601 extends RegexParsers {
val hour = s"[0-9]{2}".r ^^ {_.toInt}
val minute = s"[0-9]{2}".r ^^ {_.toInt}
val timeSep = ":"
val test = (hour ~ (timeSep ~> minute) |
hour ~ success(0) |
hour ~ minute) ^^ { case hh ~ mm => (hh, mm, 0, 0) }
}
然而,如果我加上秒和毫秒,这个词似乎非常不优雅:
val time = (hour ~ (timeSep ~> minute) ~ (timeSep ~> second) ~ (msSep ~> ms) |
hour ~ (timeSep ~> minute) ~ (timeSep ~> second) ~ success(0) |
hour ~ (timeSep ~> minute) ~ success(0) ~ success(0) |
hour ~ success(0) ~ success(0) ~ success(0) |
hour ~ minute ~ second ~ (msSep ~> ms) |
hour ~ minute ~ second ~ (msSep ~> success(0)) |
hour ~ minute ~ second ~ success(0) |
hour ~ minute ~ success(0) ~ success(0)
) ^^ {
case hh ~ mm ~ ss ~ sss =>
(hh, mm, ss, sss)
}
我没有办法解决这个问题。