2011-01-08 1 views
97

Vorrei poter trovare una corrispondenza tra la prima lettera di una parola e una delle lettere in un gruppo come "ABC". In pseudocodice, questo potrebbe essere simile:Come abbinare il pattern usando l'espressione regolare in Scala?

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

Ma come faccio a prendere la prima lettera a Scala, invece di Java? Come esprimo correttamente l'espressione regolare? È possibile farlo entro un case class?

+9

Attenzione: in Scala (e nelle lingue ML), la corrispondenza di modelli ha un'altra, molto diversa dalla regex, che significa. – delnan

+0

Probabilmente vuoi '[a-cA-C]' per quell'espressione regolare. –

+2

in scala 2.8, le stringhe sono convertite in 'Traversable' (come' List' e 'Array'), se vuoi i primi 3 caratteri, prova' "my string" .take (3) ', per il primo' "foo ".head' – shellholic

risposta

205

È possibile eseguire questa operazione poiché le espressioni regolari definiscono gli estrattori, ma è necessario prima definire il modello regex. Non ho accesso ad un REPL di Scala per testarlo, ma qualcosa del genere dovrebbe funzionare.

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

fai attenzione che non puoi dichiarare un gruppo di cattura e quindi non usarlo (cioè il caso Pattern() non corrisponde qui) –

+22

Attenzione che * devi * usare i gruppi nell'espressione regolare:' '' val Pattern = " [a-cA-C] ". r''' non funzionerà. Questo perché match-case usa '' 'unapplySeq (target: Any): Option [List [String]]' '', che restituisce i gruppi corrispondenti. – rakensi

+0

cosa significa la media '.r' alla fine di' val Pattern = ... '? –

38

Come delnan ha sottolineato, la parola chiave match in Scala non ha nulla a che fare con le espressioni regolari. Per scoprire se una stringa corrisponde a un'espressione regolare, è possibile utilizzare il metodo String.matches. Per sapere se una stringa inizia con una A, B o C, in caso inferiore o superiore, la regex sarebbe simile a questa:

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

si può leggere questo regex come "uno dei personaggi a, b, c , A, B o C seguito da qualsiasi cosa "(. significa" qualsiasi carattere "e * significa" zero o più volte ", quindi". * "È una stringa qualsiasi).

6

String.matches è il modo per eseguire la corrispondenza del modello in senso regex.

Ma come un pratico a parte, word.firstLetter nel codice reale Scala si presenta come:

word(0) 

Scala tratta stringhe come una sequenza di Char di, quindi se per qualche motivo si voleva ottenere in modo esplicito il primo carattere di la corda e abbinarlo, si potrebbe usare qualcosa di simile:

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

non sto proponendo questo come il modo generale di fare il pattern matching regex, ma è in linea con il metodo proposto per trovare prima il primo carattere di una stringa e quindi confrontarla con un'espressione regolare.

EDIT: Per essere chiari, il modo in cui vorrei fare questo è, come altri hanno detto:

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

Volevo solo mostrare un esempio più vicino possibile al vostro pseudocodice iniziale. Saluti!

+3

'' "Cat" (0) .toString'' potrebbe essere scritto più chiaramente come '' "Cat" accetta 1'', ​​imho. –

+0

Inoltre (sebbene questa sia una vecchia discussione - probabilmente sto scavando la tomba): puoi rimuovere il ". *" Dalla fine poiché non aggiunge alcun valore alla regex. Solo "Cat" .matches ("^ [a-cA-C]") – akauppi

+0

Oggi su 2.11, 'val r =" [A-Ca-c] ". R; "cat" (0) match {case r() =>} '. –

23

Per espandere un po 'sul Andrew's answer: Il fatto che le espressioni regolari definiscono estrattori possono essere utilizzati per decomporre le sottostringhe corrispondenti dalla regex molto bene utilizzando pattern matching di Scala, ad es .:

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

Sono davvero confuso dal cappello a cilindro ^. Anche se "^" significava "Abbina l'inizio della linea". Non corrisponde all'inizio della linea. –

+0

@MichaelLafayette: All'interno di una classe di caratteri ('[]'), il cursore indica la negazione, quindi '[^ \ s] 'significa' non-spazio bianco '. –

102

Dalla versione 2.10, si può utilizzare la funzione di interpolazione di stringhe di Scala:

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 

Ancora meglio si può legare le espressioni regolari:

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 

È anche possibile impostare più dettagliati meccanismi vincolanti:

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 esempio impressionante di ciò che è possibile con Dynamic è mostrato nel post del blog Introduction to Type 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") 
    } 
} 
+0

Mi è piaciuta molto la risposta, ma quando ho provato a utilizzarla al di fuori di REPL è bloccata (ad esempio, esattamente lo stesso codice che ha funzionato in REPL non funzionava nell'app in esecuzione). Inoltre c'è un problema nell'usare il segno '$' come pattern di fine riga: il compilatore si lamenta della mancanza di terminazione di stringa. – Rajish

+0

@Rajish: non so quale può essere il problema. Tutto nella mia risposta è un codice Scala valido dal 2.10. – sschaef

+0

@sschaef: that' case p.firstName.lastName.Map (... 'pattern-how on earth lo leggo? –

8

Si noti che l'approccio da @ risposta di AndrewMyers corrisponde alla stringa intera per l'espressione regolare, con l'effetto di ancorare l'espressione regolare a entrambe le estremità della stringa utilizzando ^ e $. Esempio:

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 

E senza .* alla fine:

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 
+1

Idiomaticamente, 'val MY_RE2 =" (foo | bar) ". R.unanchored; match "foo123" {case MY_RE2 (_ *) =>} '. Più idiomaticamente, 'val re' senza maiuscolo. –

4

Per prima cosa dovrebbe sapere che l'espressione regolare può essere utilizzato separatamente. Ecco un esempio:

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 

In secondo luogo dovremmo notare che la combinazione dell'espressione regolare con la corrispondenza di modelli sarebbe molto potente. Qui c'è un semplice esempio.

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

In realtà, espressione regolare è di per sé già molto potente; l'unica cosa che dobbiamo fare è renderla più potente da Scala. Ecco altri esempi nel documento Scala: http://www.scala-lang.org/files/archive/api/current/index.html#scala.util.matching.Regex