繁体   English   中英

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

[英]How to pattern match using regular expression in Scala?

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

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

但是如何在 Scala 而不是 Java 中获取第一个字母? 如何正确表达正则表达式? 是否可以在案例类中执行此操作?

您可以这样做,因为正则表达式定义了提取器,但您需要先定义正则表达式模式。 我无权访问 Scala REPL 来测试这个,但这样的事情应该可以工作。

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

从 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")
  }
}

正如德尔南指出的那样,Scala 中的match关键字与正则表达式无关。 要确定字符串是否与正则表达式匹配,您可以使用String.matches方法。 要确定字符串是以小写还是大写开头的 a、b 或 c,正则表达式将如下所示:

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

您可以将此正则表达式解读为“字符 a、b、c、A、B 或 C 之一,后跟任何字符”( .表示“任何字符”, *表示“零次或多次”,因此“.*”是任何字符串)。

稍微扩展安德鲁的回答:正则表达式定义提取器的事实可用于使用 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.
}

请注意,@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 文档中的更多示例: http : //www.scala-lang.org/files/archive/api/current/index.html#scala.util.matching.Regex

String.matches 是在正则表达式意义上进行模式匹配的方法。

但顺便提一下,实际 Scala 代码中的 word.firstLetter 如下所示:

word(0)

Scala 将字符串视为 Char 的序列,因此如果出于某种原因您想显式获取字符串的第一个字符并匹配它,您可以使用以下内容:

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

我不是建议将其作为进行正则表达式模式匹配的一般方法,但它符合您提出的首先查找字符串的第一个字符然后将其与正则表达式匹配的方法。

编辑:明确地说,我这样做的方式是,正如其他人所说:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM