Scala中Regex提取器为何在模式匹配中看似失效?
Scala正则提取器在模式匹配中失效的原因与解决办法
你碰到的这个问题,核心在于Scala正则提取器的unapply方法和findFirstIn方法的行为差异:
为什么模式匹配里会"失效"?
当你在模式匹配中写case scalaLONG(long)时,Scala会调用正则对象的unapply方法。这个方法的逻辑是:只有当正则的捕获组数量和你在模式里声明的变量数量完全匹配时,才会返回成功的匹配结果。
看你最初的正则:
val LONG = """^(0|-?[1-9][0-9]*)$""" val DOUBLE = """NaN|^-?(0(\.[0-9]*)?|([1-9][0-9]*\.[0-9]*)|(\.[0-9]+))([Ee][+-]?[0-9]+)?$"""
LONG里有2个捕获组(括号里的0和-?[1-9][0-9]*),DOUBLE里的捕获组更多,但你在模式里只写了一个变量(比如long、double),这就导致unapply无法匹配,直接走到了case _分支,所以本该识别为Double的都变成了String。
而findFirstIn方法就简单得多——它只检查整个字符串是否能被正则匹配,完全不关心捕获组的数量,所以if/else链式里能得到正确结果。
解决办法
有两种常见的修复方式:
1. 将捕获组改为非捕获组
把正则里的(...)改成(?:...),这样这些分组就不会被视为捕获组,正则整体没有捕获组后,模式匹配里用无参数的形式即可:
val LONG = """^(?:0|-?[1-9][0-9]*)$""" val DOUBLE = """^NaN|-?(?:0(?:\.[0-9]*)?|(?:[1-9][0-9]*\.[0-9]*)|(?:\.[0-9]+))(?:[Ee][+-]?[0-9]+)?$""" val scalaLONG : scala.util.matching.Regex = LONG.r val scalaDOUBLE : scala.util.matching.Regex = DOUBLE.r val types1 = Seq("abc", "3", "3.0", "-3.0E-05", "NaN").map(text => text match { case scalaLONG() => s"Long" case scalaDOUBLE() => s"Double" case _ => s"String" }) // 结果: Seq[String] = List("String", "Long", "Double", "Double", "Double")
2. 忽略所有捕获组
如果你需要保留捕获组(比如后续要提取匹配的内容),可以在模式里用_*来表示忽略所有捕获组,这样也能正确匹配:
val LONG = """^(0|-?[1-9][0-9]*)$""" val DOUBLE = """NaN|^-?(0(\.[0-9]*)?|([1-9][0-9]*\.[0-9]*)|(\.[0-9]+))([Ee][+-]?[0-9]+)?$""" val scalaLONG : scala.util.matching.Regex = LONG.r val scalaDOUBLE : scala.util.matching.Regex = DOUBLE.r val types1 = Seq("abc", "3", "3.0", "-3.0E-05", "NaN").map(text => text match { case scalaLONG(_*) => s"Long" case scalaDOUBLE(_*) => s"Double" case _ => s"String" }) // 同样得到正确结果
关键总结
- 正则提取器的
unapply方法对捕获组数量敏感:模式中变量的数量必须和正则的捕获组数量一致,或者用_*明确忽略所有捕获组。 findFirstIn仅做整体匹配检查,不处理捕获组,所以不受这个限制。
内容的提问来源于stack exchange,提问作者Richard Gomes
相关产品推荐
相关产品推荐

