如何在 Scala 中使用正则表达式进行模式匹配?

我希望能够找到一个单词的第一个字母之间的匹配,并在一组字母之一,如“ ABC”。在伪代码中,这可能类似于:

case Process(word) =>
word.firstLetter match {
case([a-c][A-C]) =>
case _ =>
}
}

但是如何在 Scala 中而不是 Java 中获取第一个字母呢?如何正确地表达正则表达式?有没有可能在 案例类内做到这一点?

183474 次浏览

正如 delnan 指出的,Scala 中的 match关键字与正则表达式无关。要查明字符串是否匹配正则表达式,可以使用 String.matches方法。要查明字符串是以大写还是小写的 a、 b 或 c 开头,正则表达式应该是这样的:

word.matches("[a-cA-C].*")

可以将这个 regex 读作“ a、 b、 c、 A、 B 或 C 中的一个字符后跟任何字符”(.表示“任何字符”,*表示“零次或更多次”,因此)。*”是任何字符串)。

您可以这样做,因为正则表达式定义提取器,但是您需要首先定义正则表达式模式。我没有访问 Scala REPL 来测试这个,但是这样的东西应该可以工作。

val Pattern = "([a-cA-C])".r
word.firstLetter match {
case Pattern(c) => c bound to capture group here
case _ =>
}

在 regex 意义上,string.match 是执行模式匹配的一种方法。

但是作为一个便利的插件,在实际的 Scala 代码中,word.firstLetters 看起来像这样:

word(0)

Scala 将 String 视为 Char 的序列,因此如果出于某种原因,您希望显式地获取 String 的第一个字符并与之匹配,您可以使用如下方法:

"Cat"(0).toString.matches("[a-cA-C]")
res10: Boolean = true

我并不是建议这种方法作为正则表达式模式匹配的一般方法,但它与您建议的方法是一致的,即首先找到字符串的第一个字符,然后将其与正则表达式匹配。

编辑: 需要说明的是,正如其他人所说的那样,我的做法是:

"Cat".matches("^[a-cA-C].*")
res14: Boolean = true

只是想展示一个尽可能接近您的初始伪代码的示例!

安德鲁的回答上稍微展开一下: 正则表达式定义提取器这一事实可以用来分解正则表达式所匹配的子字符串,使用 Scala 的模式匹配,例如:

val Process = """([a-cA-C])([^\s]+)""".r // define first, rest is non-space
for (p <- Process findAllIn "aha bah Cah dah") p match {
case Process("b", _) => println("first: 'a', some rest")
case Process(_, rest) => println("some first, rest: " + rest)
// etc.
}

从2.10版本开始,我们就可以使用 Scala 的字符串插值特性:

implicit class RegexOps(sc: StringContext) {
def r = new util.matching.Regex(sc.parts.mkString, sc.parts.tail.map(_ => "x"): _*)
}


scala> "123" match { case r"\d+" => true case _ => false }
res34: Boolean = true

更好的方法是绑定正则表达式组:

scala> "123" match { case r"(\d+)$d" => d.toInt case _ => 0 }
res36: Int = 123


scala> "10+15" match { case r"(\d\d)${first}\+(\d\d)${second}" => first.toInt+second.toInt case _ => 0 }
res38: Int = 25

还可以制定更详细的约束机制:

scala> object Doubler { def unapply(s: String) = Some(s.toInt*2) }
defined module Doubler


scala> "10" match { case r"(\d\d)${Doubler(d)}" => d case _ => 0 }
res40: Int = 20


scala> object isPositive { def unapply(s: String) = s.toInt >= 0 }
defined module isPositive


scala> "10" match { case r"(\d\d)${d @ isPositive()}" => d.toInt case _ => 0 }
res56: Int = 10

一个令人印象深刻的例子就是 Dynamic的可能性,它显示在博客文章 类型动态简介中:

object T {


class RegexpExtractor(params: List[String]) {
def unapplySeq(str: String) =
params.headOption flatMap (_.r unapplySeq str)
}


class StartsWithExtractor(params: List[String]) {
def unapply(str: String) =
params.headOption filter (str startsWith _) map (_ => str)
}


class MapExtractor(keys: List[String]) {
def unapplySeq[T](map: Map[String, T]) =
Some(keys.map(map get _))
}


import scala.language.dynamics


class ExtractorParams(params: List[String]) extends Dynamic {
val Map = new MapExtractor(params)
val StartsWith = new StartsWithExtractor(params)
val Regexp = new RegexpExtractor(params)


def selectDynamic(name: String) =
new ExtractorParams(params :+ name)
}


object p extends ExtractorParams(Nil)


Map("firstName" -> "John", "lastName" -> "Doe") match {
case p.firstName.lastName.Map(
Some(p.Jo.StartsWith(fn)),
Some(p.`.*(\\w)$`.Regexp(lastChar))) =>
println(s"Match! $fn ...$lastChar")
case _ => println("nope")
}
}

注意,@AndrewMyers 的答案中的方法将 完整的字符串与正则表达式匹配,其效果是使用 ^$将正则表达式锚定在字符串的两端。例如:

scala> val MY_RE = "(foo|bar).*".r
MY_RE: scala.util.matching.Regex = (foo|bar).*


scala> val result = "foo123" match { case MY_RE(m) => m; case _ => "No match" }
result: String = foo


scala> val result = "baz123" match { case MY_RE(m) => m; case _ => "No match" }
result: String = No match


scala> val result = "abcfoo123" match { case MY_RE(m) => m; case _ => "No match" }
result: String = No match

最后没有 .*:

scala> val MY_RE2 = "(foo|bar)".r
MY_RE2: scala.util.matching.Regex = (foo|bar)


scala> val result = "foo123" match { case MY_RE2(m) => m; case _ => "No match" }
result: String = No match

首先,我们应该知道正则表达式可以单独使用:

import scala.util.matching.Regex
val pattern = "Scala".r // <=> val pattern = new Regex("Scala")
val str = "Scala is very cool"
val result = pattern findFirstIn str
result match {
case Some(v) => println(v)
case _ =>
} // output: Scala

其次,我们应该注意到将正则表达式和模式匹配结合起来会非常强大。

val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r
"2014-11-20" match {
case date(year, month, day) => "hello"
} // output: hello

事实上,正则表达式本身已经非常强大了; 我们唯一需要做的就是通过 Scala 使其更强大。以下是 Scala Document: http://www.scala-lang.org/files/archive/api/current/index.html#scala.util.matching.Regex中的更多示例