如何在Scala中检查字符串是否完全匹配正则表达式?

84
假设我有一个正则表达式模式,我想匹配许多字符串。
val Digit = """\d""".r

我只想检查给定的字符串是否完全匹配正则表达式。在Scala中,有什么好的和惯用的方法来做到这一点?

我知道我可以对正则表达式进行模式匹配,但在这种情况下,语法上并不是很美观,因为我没有要提取的组:

scala> "5" match { case Digit() => true case _ => false }
res4: Boolean = true

或者我可以退回到底层的Java模式:

scala> Digit.pattern.matcher("5").matches
res6: Boolean = true

这种方法也不太优雅。

有更好的解决方案吗?


我认为 "5" match { case Digit() => true case _ => false } 看起来比使用底层模式对象更好。 - Mygod
6个回答

71

回答自己的问题,我将使用“ pimp my library pattern ”

object RegexUtils {
  implicit class RichRegex(val underlying: Regex) extends AnyVal {
    def matches(s: String) = underlying.pattern.matcher(s).matches
  }
}

并像这样使用它

import RegexUtils._
val Digit = """\d""".r
if (Digit matches "5") println("match")
else println("no match")

除非有人提出更好(标准)的解决方案。

  • 我没有提升String的范围以限制潜在的副作用。

  • unapplySeq在那个上下文中不太容易理解。


你有没有想到任何特定的副作用?我改用了 String,目前看来这个方法很好用,尽管 String 的成员函数 matches(regex: String) 存在。 - KajMagnus
1
我也使用了一个名为misses的函数。匹配和不匹配 :-) 需要写!s.matches(r)而不是s misses r真是太烦人了。 - KajMagnus
2
@polygenelubricants 建议的内置 "5" matches "\\d" 怎么样? - Erik Kaplun
2
数据符合模式,而不是相反。Regex的scaladoc非常强调“匹配”缺乏布尔值。个人认为,您已经用笨重的if-else替换了漂亮的匹配。如果您不关心组,请使用case r(_ *)=> - som-snytt
1
必须有一种方法可以在不导入外部库的情况下完成这个任务... - Jameela Huq
2
@JameelaHuq 访问这个问题的人会对2.13感到满意,因为正则表达式终于得到匹配。https://github.com/scala/scala/pull/6521 - som-snytt

61

26
可以,但缺点是每次尝试匹配时都要编译模式。出于性能原因,我希望避免这种情况。 - mkneissl
3
看起来你的.pattern.matcher(text).matches是正确的方法。如果Scala支持,你可以将这种冗长的操作封装到某个实用方法或重载运算符中。 - polygenelubricants
4
谢谢,那就是我要做的,看看我的回答。我希望在Stack Overflow上回答自己的问题是被接受的行为...Meta上说可以... - mkneissl
2
@ed. 那样做甚至更慢,更臃肿,为什么呢? - Erik Kaplun
2
参考链接已失效。 - Valy Dia

15

如果要完全匹配,可以使用unapplySeq方法。该方法尝试匹配目标(整个匹配)并返回匹配项。

scala> val Digit = """\d""".r
Digit: scala.util.matching.Regex = \d

scala> Digit unapplySeq "1"
res9: Option[List[String]] = Some(List())

scala> Digit unapplySeq "123"
res10: Option[List[String]] = None

scala> Digit unapplySeq "string"
res11: Option[List[String]] = None

5
虽然如此,unapply和unapplySeq的主要用途是隐式地用于match块中的case语句。 - Randall Schulz

12
  """\d""".r.unapplySeq("5").isDefined            //> res1: Boolean = true
  """\d""".r.unapplySeq("a").isDefined            //> res2: Boolean = false

嗯,为什么要在两年后发布与https://dev59.com/6HA75IYBdhLWcg3w794Y#3022478重复的内容? - mkneissl
3
您最初的问题要求结果以“true”或“false”结尾,而不是“Some”或“None”。据我所知,2年前isDefined不是库的一部分,但也许是。无论如何,我的答案不是重复的;-) - Jack
1
我明白了,这不是重复的。抱歉。 - mkneissl
2
没问题;-) 我的错误,我应该解释一下为什么在我的回答中使用isDefined。仅仅给出代码作为答案通常是一个坏主意,所以这是我的错。 - Jack

1

0

答案在正则表达式中:

val Digit = """^\d$""".r

然后使用现有方法之一。

3
我认为这里的问题不在于锚点。至少在Java中,String/Pattern/Matcher.matches已经是整个字符串匹配了。我认为问题只是Scala正则表达式的风格/惯用法,即那些“现有方法”的含义是什么。 - polygenelubricants
@polygenelubricants 嗯,Matcher.matches 是个例外。好吧,它能实现一些优化,尽管我不确定 Java 库是否真的利用了它。但是正则表达式表达需求完全匹配的“标准”方式是使用锚点。由于 Scala 库没有提供完全匹配方法,因此正确的做法是使用锚点。要么这样,要么使用 Java 库。 - Daniel C. Sobral
定位并不是问题。请参见Vasil答案中的“123”示例。 - mkneissl
@mkneissl,使用 """^\d$""".rfindFirstIn 与使用 """\d""".runapplySeq 有什么不同之处? - Daniel C. Sobral
5
你可能没有领会重点 - 我的问题是,如果我只需要知道一个正则表达式是否完全匹配,Scala 中有什么好的方法来表示这一点。尽管有很多可行的解决方案,但总体而言,我认为在 Regex 中缺少一个只执行此操作而不做其他操作的方法。 回答你评论中的问题:unapplySeq 和 findFirstMatch 的区别在于,我必须更改 Regex 来添加锚定符号。两种方法都不能立即表达我的意图,也不返回布尔值,也就是说我必须从 Option 转换为 Boolean(没有问题,但会增加更多混乱)。 - mkneissl
1
@mkneissl 我不喜欢Java的matches概念,但还好。至于OptionBoolean,在末尾添加nonEmpty即可获得Boolean - Daniel C. Sobral

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接