Wie kann ich überprüfen, ob ein String vollständig mit einem Regex in Scala übereinstimmt?

80

Angenommen, ich habe ein Regex-Muster, dem ich viele Strings zuordnen möchte.

val Digit = """\d""".r

Ich möchte nur überprüfen, ob ein bestimmter String vollständig mit dem Regex übereinstimmt. Was ist ein guter und idiomatischer Weg, dies in Scala zu tun?

Ich weiß, dass ich Muster-Übereinstimmungen mit Regexes erstellen kann, aber dies ist syntaktisch in diesem Fall nicht sehr erfreulich, da ich keine Gruppen zum Extrahieren habe:

scala> "5" match { case Digit() => true case _ => false }
res4: Boolean = true

Oder ich könnte auf das zugrunde liegende Java-Muster zurückgreifen:

scala> Digit.pattern.matcher("5").matches
res6: Boolean = true

das ist auch nicht elegant.

Gibt es eine bessere Lösung?

mkneissl
quelle
Ich denke, "5" match { case Digit() => true case _ => false }sieht besser aus als das zugrunde liegende Musterobjekt zu verwenden.
Mygod

Antworten:

66

Bei der Beantwortung meiner eigenen Frage verwende ich das Muster "Zuhälter meiner Bibliothek".

object RegexUtils {
  implicit class RichRegex(val underlying: Regex) extends AnyVal {
    def matches(s: String) = underlying.pattern.matcher(s).matches
  }
}

und benutze es so

import RegexUtils._
val Digit = """\d""".r
if (Digit matches "5") println("match")
else println("no match")

es sei denn, jemand findet eine bessere (Standard-) Lösung.

Anmerkungen

  • Ich habe nicht gepimpt String, um den Umfang möglicher Nebenwirkungen einzuschränken.

  • unapplySeq liest sich in diesem Zusammenhang nicht sehr gut.

mkneissl
quelle
Hatten Sie eine bestimmte Nebenwirkung im Sinn? Ich habe Stringstattdessen gepimpt , und das funktioniert bisher trotz der StringMitgliedsfunktion einwandfrei matches(regex: String).
KajMagnus
1
Ich habe auch mit einer Funktion missesgepimpt. Match und Missmatch :-) Es ist so nervig, !s.matches(r)statt schreiben zu müssen s misses r. Hmm
KajMagnus
1
Wie wäre es mit dem eingebauten "5" matches "\\d"@polygenelubricants vorgeschlagen?
Erik Kaplun
2
Daten stimmen mit einem Muster überein, nicht umgekehrt. Der Scaladoc auf Regex macht eine große Sache über das Fehlen eines Booleschen Werts für "Übereinstimmungen". Persönlich denke ich, dass Sie ein schönes Match gegen ein klobigeres getauscht haben, wenn-sonst. Wenn Sie sich nicht für Gruppen interessieren, verwenden Sie case r(_*) =>.
Som-Snytt
Es muss eine Möglichkeit geben, dies zu tun, ohne eine externe Bibliothek zu importieren ...
Jameela Huq
56

Ich kenne Scala nicht so gut, aber es sieht so aus, als ob Sie einfach Folgendes tun können:

"5".matches("\\d")

Verweise

Polygenschmierstoffe
quelle
25
Nun, das funktioniert, hat aber den Nachteil, dass das Muster bei jedem Übereinstimmungsversuch kompiliert wird. Ich möchte das aus Leistungsgründen vermeiden.
mkneissl
3
@mkneissl: dann sieht es so aus, als ob du .pattern.matcher(text).matchesder richtige Weg bist . Sie können die Ausführlichkeit unter einer Dienstprogrammmethode oder einem überladenen Operator oder etwas anderem ausblenden, wenn Scala dies unterstützt.
Polygenelubricants
4
Danke, das werde ich tun, siehe meine Antwort. Ich hoffe, die Beantwortung der eigenen Fragen ist ein akzeptiertes Verhalten bei Stapelüberlauf ... Meta sagt, es ist ...
mkneissl
2
@ed. das ist noch langsamer und knuspriger, warum also?
Erik Kaplun
Der als Referenz angegebene Link ist defekt
Valy Dia
13

Für die vollständige Übereinstimmung können Sie unapplySeq verwenden . Diese Methode versucht, das Ziel (ganze Übereinstimmung) abzugleichen und gibt die Übereinstimmungen zurück.

scala> val Digit = """\d""".r
Digit: scala.util.matching.Regex = \d

scala> Digit unapplySeq "1"
res9: Option[List[String]] = Some(List())

scala> Digit unapplySeq "123"
res10: Option[List[String]] = None

scala> Digit unapplySeq "string"
res11: Option[List[String]] = None
Vasil Remeniuk
quelle
4
Während true, liegt die primäre Verwendung von unapply und unapplySeq implizit im cases eines matchBlocks.
Randall Schulz
11
  """\d""".r.unapplySeq("5").isDefined            //> res1: Boolean = true
  """\d""".r.unapplySeq("a").isDefined            //> res2: Boolean = false
Jack
quelle
Hmm. Warum zwei Jahre später ein Duplikat von stackoverflow.com/a/3022478/158823 veröffentlichen ?
mkneissl
2
In Ihrer ursprünglichen Frage wurde nach einem Ergebnis gefragt, das mit "wahr" oder "falsch" endet, nicht mit "Einige" oder "Keine". Soweit mir bekannt ist, war isDefined vor 2 Jahren nicht Teil der Bibliothek, aber vielleicht war es das auch. Wie auch immer, meine Antwort ist kein Duplikat ;-)
Jack
Ich verstehe, es ist kein Duplikat. Es tut uns leid.
mkneissl
1
Keine Probleme ;-) Mein Fehler, ich hätte erklären sollen, warum ich in meiner Antwort isDefined verwende. Nur Code als Antwort zu geben, ist im Allgemeinen eine schlechte Idee, also ist es meine schlechte.
Jack
1

Die Antwort ist in der Regex:

val Digit = """^\d$""".r

Verwenden Sie dann eine der vorhandenen Methoden.

Daniel C. Sobral
quelle
3
Ich denke nicht, dass Anker hier das Problem sind. String/Pattern/Matcher.matchesZumindest in Java stimmt die gesamte Zeichenfolge bereits überein. Ich denke, das Problem ist nur Stil / Redewendung für Regexing in Scala, dh was diese "eine der vorhandenen Methoden" sind.
Polygenelubricants
@polygenelubricants Nun, Matcher.matchesist eine Aberration. Ok, es macht einige Optimierungen möglich, obwohl ich nicht weiß, ob die Java-Bibliothek tatsächlich davon profitiert. Die Standardmethode für reguläre Ausdrücke, um auszudrücken, dass eine vollständige Übereinstimmung erforderlich ist, ist die Verwendung von Ankern. Da die Scala Bibliothek ist nicht bietet eine vollständige Match - Methode, dann dem richtigen Weg , es zu tun ist , Anker zu verwenden. Entweder das, oder Sie verwenden die Java-Bibliothek.
Daniel C. Sobral
Verankerung ist nicht das Problem. Siehe auch das Beispiel "123" in Vasils Antwort.
mkneissl
5
@ Daniel Sie könnten den Punkt verfehlen - Meine Frage war, wenn ich nur wissen muss, ob eine Regex vollständig übereinstimmt, was eine gute Möglichkeit ist, dies in Scala auszudrücken. Es gibt viele funktionierende Lösungen, aber zusammenfassend denke ich, dass in Regex eine Methode fehlt, die genau das tut und sonst nichts. Um die Frage in Ihrem Kommentar zu beantworten: Der Unterschied zwischen unapplySeq und findFirstMatch besteht darin, dass ich den Regex ändern muss, um die Anker hinzuzufügen. Beide Methoden drücken weder sofort meine Absicht aus, noch geben sie einen booleschen Wert zurück, dh ich müsste von Option zu Boolesch wechseln (kein Problem, aber mehr Unordnung hinzufügen).
mkneissl
1
@mkneissl Ich mag das Konzept von Java nicht matches, aber ok. Was Optionvs betrifft Boolean, füge nonEmptyes am Ende hinzu und du bekommst das Boolean.
Daniel C. Sobral
0

Verwenden der Standard-Scala-Bibliothek und eines vorkompilierten Regex-Musters und Musterabgleichs (Stand der Technik):

val digit = """(\d)""".r

"2" match {
  case digit( a) => println(a + " is Digit")
  case _ => println("it is something else")
}

mehr zu lesen: http://www.scala-lang.org/api/2.12.1/scala/util/matching/index.html

Sven
quelle