17 Stimmen

Scala regexps: wie man Treffer als Array oder Liste zurückgibt

Gibt es eine einfache Möglichkeit, Regex-Treffer als Array zurückzugeben?
So versuche ich es in 2.7.7:

val s = """6 1 2"""
val re = """(\d+)\s(\d+)\s(\d+)""".r
for (m <- re.findAllIn(s)) println(m) // gibt "6 1 2" aus
re.findAllIn(s).toList.length // 3? Nein! Es gibt 1 zurück!

Aber dann habe ich es versucht:

s match {
  case re(m1, m2, m3) => println(m1)
}

Und das funktioniert gut! m1 ist 6, m2 ist 1, etc.

Dann fand ich etwas, das meine Verwirrung noch verstärkte:

val mit = re.findAllIn(s)
println(mit.toString)
println(mit.length)
println(mit.toString)

Das gibt aus:

nicht-leerer Iterator
1
leerer Iterator

Der "length"-Aufruf ändert auf irgendwie den Zustand des Iterators. Was passiert hier?

0 Stimmen

Dein Aufruf von findAllIn(s) stimmt mit der gesamten Zeichenfolge überein, so dass deine resultierende Liste nicht List(6 1 2) ist, sondern wirklich List("6 1 2") der Länge 1

29voto

Daniel C. Sobral Punkte 290004

Ok, zuerst ist es wichtig zu verstehen, dass findAllIn ein Iterator zurückgibt. Ein Iterator ist ein einmal konsumierbares, veränderliches Objekt. ALLES, was du damit machst, wird es verändern. Informiere dich über Iterators, wenn du nicht vertraut mit ihnen bist. Wenn du möchtest, dass es wiederverwendbar ist, dann konvertiere das Ergebnis von findAllIn in eine List und benutze nur diese List.

Jetzt scheint es, dass du alle übereinstimmenden Gruppen möchtest, nicht alle Treffer. Die Methode findAllIn wird alle Treffer des vollen Regex zurückgeben, die im String gefunden werden können. Zum Beispiel:

scala> val s = """6 1 2, 4 1 3"""
s: java.lang.String = 6 1 2, 4 1 3

scala> val re = """(\d+)\s(\d+)\s(\d+)""".r
re: scala.util.matching.Regex = (\d+)\s(\d+)\s(\d+)

scala> for(m <- re.findAllIn(s)) println(m)
6 1 2
4 1 3

Siehst du, dass es zwei Treffer gibt und keiner von ihnen das ", " in der Mitte des Strings enthält, da es kein Teil eines Treffers ist.

Wenn du die Gruppen haben möchtest, kannst du sie so erhalten:

scala> val s = """6 1 2"""
s: java.lang.String = 6 1 2

scala> re.findFirstMatchIn(s)
res4: Option[scala.util.matching.Regex.Match] = Some(6 1 2)

scala> res4.get.subgroups
res5: List[String] = List(6, 1, 2)

Oder, indem du findAllIn verwendest, so:

scala> val s = """6 1 2"""
s: java.lang.String = 6 1 2

scala> for(m <- re.findAllIn(s).matchData; e <- m.subgroups) println(e)
6
1
2

Die Methode matchData wird einen Iterator erzeugen, der Match anstatt von String zurückgibt.

10voto

Rex Kerr Punkte 164629

Es gibt einen Unterschied darin, wie unapplySeq mehrere Gruppen interpretiert und wie findAllIn dies tut. findAllIn sucht nach Ihrem Muster im String und gibt jeden übereinstimmenden String zurück (und geht bei Erfolg um das Übereinstimmen vor oder um ein Zeichen bei Misserfolg).

Also zum Beispiel:

scala> val s = "gecko 6 1 2 3 4 5"
scala> re.findAllIn(s).toList
res3: List[String] = List(6 1 2, 3 4 5)

Andererseits nimmt unapplySeq eine perfekte Übereinstimmung mit der Sequenz an.

scala> re.unapplySeq(s)
res4: Option[List[String]] = None

Wenn Sie also Gruppen analysieren möchten, die Sie in einem genauen Regex-String festgelegt haben, verwenden Sie unapplySeq. Wenn Sie die Teilzeichenfolgen finden möchten, die wie Ihr Regex-Muster aussehen, verwenden Sie findAllIn. Wenn Sie beides tun möchten, verketten Sie sie selbst:

scala> re.findAllIn(s).flatMap(text => re.unapplySeq(text).elements )
res5: List[List[String]] = List(List(6, 1, 2), List(3, 4, 5))

2voto

Mitch Blevins Punkte 13056

Versuch es mal:

  val s = """6 1 2"""
  val re = """\d+""".r
  println(re.findAllIn(s).toList) // List(6, 1, 2)
  println(re.findAllIn(s).toList.length) // 3

Und wenn du wirklich eine Liste der Treffergruppen innerhalb eines einzelnen Regex benötigst:

  val s = """6 1 2"""
  val Re = """(\d+)\s(\d+)\s(\d+)""".r
  s match {  // das ist nur eine Abkürzung für Re.unapplySeq(s)
      case Re(mg@_*) => println(mg) // List(6, 1, 2)
  }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X