135 votes

Expression régulière et le pattern matching in Scala

Je voudrais être en mesure de trouver une correspondance entre la première lettre d'un mot, et l'une des lettres dans un groupe comme "ABC". En pseudo-code, cela pourrait ressembler à quelque chose comme:

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

Mais comment dois-je saisir la première lettre en Scala au lieu de Java? Comment puis-je exprimer l'expression régulière correctement? Est-il possible de faire cela à l'intérieur d'une classe de cas?

254voto

Andrew Myers Points 5403

Vous pouvez faire cela parce que les expressions régulières définir les extracteurs, mais vous devez définir les regex modèle premier. Je n'ai pas accès à un Scala REPL à tester, mais quelque chose comme cela devrait fonctionner.

val Pattern = "([a-cA-C])".r
mot.firstLetter match {
 cas Motif(c) => c lié à la capture de groupe ici
 cas _ =>
}

130voto

sschaef Points 20242

Depuis la version 2.10, on peut utiliser la Scala, à la chaîne d'interpolation de fonction:

implicit class Regex(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

Même mieux que l'on peut lier expression régulière groupes:

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

Il est également possible de définir plus détaillée des mécanismes de liaison:

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

Un exemple impressionnant sur ce qui est possible avec Dynamic est indiqué dans le billet de blog de l'Introduction de Type Dynamique:

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

51voto

sepp2k Points 157757

Comme delnan souligné, l' match mot-clé dans la Scala n'a rien à voir avec les regexes. Pour savoir si une chaîne correspond à une expression régulière, vous pouvez utiliser l' String.matches méthode. Pour savoir si une chaîne commence par un a, b ou c en minuscule ou majuscule, la regex devrait ressembler à ceci:

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

Vous pouvez lire cette expression comme "l'un des caractères a, b, c, A, B ou C, suivie par quoi que ce soit" (. signifie "n'importe quel caractère" et * signifie "zéro fois ou plus",".*" est une chaîne).

28voto

Fabian Steeg Points 24261

Pour développer un peu sur Andrew réponse: Le fait que les expressions régulières définir les extracteurs peuvent être utilisés pour décomposer les sous-chaînes compensée par la regex très bien à l'aide de la Scala de filtrage, par exemple:

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.
}

9voto

mikhail_b Points 367

Notez que l'approche de @AndrewMyers la réponse correspond à l' ensemble de la chaîne de l'expression régulière, avec l'effet de l'ancrage de l'expression régulière aux deux extrémités de la chaîne à l'aide de ^ et $. Exemple:

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

Et sans .* à la fin:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X