Jak sprawdzić, czy ciąg znaków w pełni pasuje do wyrażenia regularnego w Scali?

80

Załóżmy, że mam wzór Regex, do którego chcę dopasować wiele ciągów.

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

Chcę tylko sprawdzić, czy dany ciąg w pełni pasuje do wyrażenia regularnego. Jaki jest dobry i idiomatyczny sposób na zrobienie tego w Scali?

Wiem, że mogę dopasować wzorce na Regexes, ale w tym przypadku nie jest to zbyt przyjemne, ponieważ nie mam grup do wyodrębnienia:

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

Albo mogę wrócić do podstawowego wzorca Java:

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

co też nie jest eleganckie.

Czy jest lepsze rozwiązanie?

mkneissl
źródło
Myślę, że "5" match { case Digit() => true case _ => false }wygląda lepiej niż użycie podstawowego obiektu wzorca.
Mygod

Odpowiedzi:

66

Odpowiadając na własne pytanie, użyję wzoru „odpicuj moją bibliotekę”

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

i używaj go w ten sposób

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

chyba że ktoś wymyśli lepsze (standardowe) rozwiązanie.

Uwagi

  • Nie starałem Stringsię ograniczyć zakresu potencjalnych skutków ubocznych.

  • unapplySeq nie czyta zbyt dobrze w tym kontekście.

mkneissl
źródło
Czy miałeś na myśli jakiś szczególny efekt uboczny? StringZamiast tego pimpedowałem i jak dotąd działa to dobrze, pomimo Stringfunkcji członka matches(regex: String).
KajMagnus,
1
Pimperowałem też z funkcją misses. Match and missmatch :-) To takie irytujące, że trzeba pisać !s.matches(r)zamiast pisać s misses r. Hmm
KajMagnus
1
A co z wbudowanym, "5" matches "\\d"który zasugerował @polygenelubricants?
Erik Kaplun
2
Dane pasują do wzorca, a nie odwrotnie. Scaladoc w Regex robi wielką sprawę z powodu braku wartości logicznej dla „dopasowań”. Osobiście uważam, że zamieniłeś fajny mecz na bardziej clunks, jeśli - inaczej. Jeśli nie zależy Ci na grupach, użyj case r(_*) =>.
som-snytt
Musi istnieć sposób, aby to zrobić bez importowania zewnętrznej biblioteki ...
Jameela Huq,
56

Nie znam Scali aż za dobrze, ale wygląda na to, że możesz po prostu zrobić:

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

Bibliografia

smary wielogenowe
źródło
25
Cóż, to działa, ale ma tę wadę, że wzorzec jest kompilowany przy każdej próbie dopasowania. Chciałbym tego uniknąć ze względu na wydajność.
mkneissl
3
@mkneissl: wtedy wygląda na to, że Twoja .pattern.matcher(text).matchesjest droga. Możesz ukryć gadatliwość pod jakąś metodą narzędzia lub przeciążonym operatorem lub czymś podobnym, jeśli Scala to obsługuje.
polygenelubricants
4
Dzięki, właśnie to zrobię, zobacz moją odpowiedź. Mam nadzieję, że odpowiadanie na własne pytania jest akceptowanym zachowaniem na Stack Overflow ... Meta mówi, że jest ...
mkneissl
2
@ed. to jest jeszcze wolniejsze i bardziej agresywne, więc dlaczego?
Erik Kaplun
Link podany jako odniesienie jest uszkodzony
Valy Dia
13

Aby uzyskać pełne dopasowanie, możesz użyć unapplySeq . Ta metoda próbuje dopasować cel (całe dopasowanie) i zwraca dopasowania.

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
źródło
4
Chociaż prawda, podstawowe użycie unapply i unapplySeq jest niejawnie w cases matchbloku.
Randall Schulz
11
  """\d""".r.unapplySeq("5").isDefined            //> res1: Boolean = true
  """\d""".r.unapplySeq("a").isDefined            //> res2: Boolean = false
Jacek
źródło
Hmm. Po co publikować duplikat stackoverflow.com/a/3022478/158823 dwa lata później?
mkneissl
2
Twoje pierwotne pytanie dotyczyło wyniku kończącego się na „prawda” lub „fałsz”, a nie „Niektóre” lub „Brak”. O ile wiem, to Defined nie był częścią biblioteki 2 lata temu, ale może tak było. W każdym razie moja odpowiedź nie jest duplikatem ;-)
Jack
Rozumiem, to nie jest duplikat. Przepraszam.
mkneissl
1
Nie ma sprawy ;-) Mój błąd, powinienem był wyjaśnić, dlaczego używam isDefined w mojej odpowiedzi. Samo podanie kodu jako odpowiedzi jest generalnie złym pomysłem, więc to mój zły pomysł.
Jack
1

Odpowiedź znajduje się w wyrażeniu regularnym:

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

Następnie użyj jednej z istniejących metod.

Daniel C. Sobral
źródło
3
Nie sądzę, żeby problem dotyczył kotwic. String/Pattern/Matcher.matches, przynajmniej w Javie, jest już dopasowany cały ciąg. Myślę, że chodzi tylko o styl / idiom dla regexingu w Scali, czyli czym jest ta „jedna z istniejących metod”.
polygenelubricants
@polygenelubricants Cóż, Matcher.matchesto aberracja. Ok, umożliwia to pewne optymalizacje, chociaż nie wiem, czy biblioteka Java faktycznie to wykorzystuje. Ale standardowym sposobem wyrażenia w wyrażeniach regularnych, że wymagane jest pełne dopasowanie, jest użycie kotwic. Ponieważ biblioteka Scala nie zapewnia metody pełnego dopasowania, właściwym sposobem jest użycie kotwic. Albo to, albo skorzystaj z biblioteki Java.
Daniel C. Sobral
Kotwiczenie nie jest problemem. Zobacz także przykład „123” w odpowiedzi Wasila.
mkneissl
5
@Daniel Być może nie rozumiesz - Moje pytanie brzmiało: jeśli chcę tylko wiedzieć, czy wyrażenie regularne jest w pełni zgodne, jaki jest dobry sposób wyrażenia tego w Scali. Istnieje wiele działających rozwiązań, ale podsumowując, myślę, że w Regex brakuje metody, która po prostu robi to i nic więcej. Odpowiadając na pytanie w Twoim komentarzu: różnica między unapplySeq a findFirstMatch polega na tym, że muszę zmienić Regex, aby dodać kotwice. Obie metody ani nie wyrażają od razu mojego zamiaru, ani nie zwracają wartości logicznej, to znaczy musiałbym przejść od Option do Boolean (nie ma problemu, ale dodam więcej bałaganu).
mkneissl
1
@mkneissl Nie podoba mi się koncepcja Javy matches, ale ok. Jeśli chodzi o Optionvs Boolean, dodaj nonEmptydo końca, a otrzymasz Boolean.
Daniel C. Sobral
0

Korzystanie z biblioteki Standard Scala i wstępnie skompilowanego wzorca wyrażenia regularnego i dopasowania do wzorca (co jest najnowocześniejszym standardem Scala):

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

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

więcej do przeczytania: http://www.scala-lang.org/api/2.12.1/scala/util/matching/index.html

Sven
źródło