以下是我为positiveInt
s:编写一个小型解析器的尝试
import scala.util.parsing.combinator.RegexParsers
object PositiveIntParser extends RegexParsers {
private def positiveInt: Parser[Int] = """0*[1-9]d*""".r ^^ { _.toInt }
def apply(input: String): Option[Int] = parseAll(positiveInt, input) match {
case Success(result, _) => Some(result)
case _ => None
}
}
问题是,如果输入字符串太长,toInt
会抛出一个NumberFormatException
,这会使我的解析器崩溃:
scala> :load PositiveIntParser.scala
Loading PositiveIntParser.scala...
import scala.util.parsing.combinator.RegexParsers
defined object PositiveIntParser
scala> PositiveIntParser("12")
res0: Option[Int] = Some(12)
scala> PositiveIntParser("-12")
res1: Option[Int] = None
scala> PositiveIntParser("123123123123123123")
java.lang.NumberFormatException: For input string: "123123123123123123"
at ...
相反,当toInt
抛出异常时,我希望我的positiveInt
解析器能够正常地失败(通过返回Failure
)。我该怎么做?
我想到的一个简单的解决方案是限制regex所接受的字符串的长度,但这并不令人满意。
我猜scala.util.parsing.combinator
库已经为这个用例提供了一个解析器组合子,但我一直找不到。。。
您可以使用接受部分函数的组合子(受如何使scala解析器失败的启发):
private def positiveInt: Parser[Int] = """0*[1-9]d*""".r ^? {
case x if Try(x.toInt).isSuccess => x.toInt
}
如果你想避免双重转换,你可以创建一个提取器来执行匹配和转换:
object ParsedInt {
def unapply(str: String): Option[Int] = Try(str.toInt).toOption
}
private def positiveInt: Parser[Int] = """0*[1-9]d*""".r ^? { case ParsedInt(x) => x }
也可以将阳性测试转移到案例条件中,我发现它比一个有点复杂的正则表达式更可读:
private def positiveInt: Parser[Int] = """d+""".r ^? { case ParsedInt(x) if x > 0 => x }
根据您的意见,提取也可以在单独的^^
步骤中执行,如下所示:
private def positiveInt: Parser[Int] = """d+""".r ^^
{ str => Try(str.toInt)} ^? { case util.Success(x) if x > 0 => x }
用Try()
包装对parseAll
的调用如何?
Try(parseAll(positiveInt, input))
scala.util.Try
的apply
方法将在Failure[T]
中包装任何异常,然后您甚至可以使用.toOption
将任何Failure
转换为None
。
Try(parseAll(positiveInt, input)).toOption