2012-12-11 15 views
6

W Ruby, jeśli mam dwóch wyrażeń regularnych, mam możliwość stworzenia innego wyrażenia regularnego tak:Scala Regex unia

a = /\d+/ # Matches digits 
b = /\s+/ # Matches whitespaces 
c = Regexp.union(a, b) # Matches sequences that consist only of digits or only of whitespaces 

chcę zrobić to samo w Scala, ale nie znaleźliśmy się jak mogłem to zrobić. Zauważ, że nie pytam o składnię, aby utworzyć połączenie klas znaków, takich jak (\d+)|(\s+) w poprzednim przykładzie. Naprawdę szukam możliwości utworzenia nowego Regexp z dwóch danych Regexp.

Właściwie, w końcu nie zrobię tego tylko dla dwóch Regeksów, ale dużej liczby. Nie dbam o grupowanie ani nic, po prostu chcę wiedzieć, czy ciąg pasuje do jednej z listy podanych Regeksów. Mogłem po prostu sprawdzić wszystkie z nich w pętli, ale to zbyt nieefektywne, dlatego potrzebuję jednego Regexp, aby sprawdzić związek.

+1

Jeśli chcesz dopasować * to * OR * to *, musisz użyć opcji Alternation '|'. – stema

+1

Jeśli w Scali nie ma metody "związkowej", możesz pobrać wzorzec używany przez każde wyrażenie regularne, a następnie połączyć je ręcznie, jak '(regex1) | (regex2)' i utworzyć nowe wyrażenie regularne z wyniku. – Vulcan

+0

@stema Tak, wiem, jeśli tworzę Regexps, to używam |, ale jeśli mam już dwa Regexps i chcę je złączyć, potrzebuję czegoś innego. – Lykos

Odpowiedz

7

Scala używa mechanizmu regex Java, który opiera się na klasie java.util.regex.Pattern. Pattern ma dokładnie jedną metodę, która może utworzyć regex:

public static Pattern compile(String regex) 

to wszystko, i Scala nie daje żadnych istotnych ulepszeń.

Ale jedno można zrobić, to skorzystać z wbudowanego w unioning w sprawozdaniach meczu, tutaj pokazane z przechwytywanie grup w przypadku, gdy chcesz wyciągnąć coś z napisu:

val Dig = """(\d+)""".r 
val Wsp = """(\s+)""".r 

scala> "45" match { case Dig(_) | Wsp(_) => println("found"); case _ => } 

znaleźć

scala> " " match { case Dig(_) | Wsp(_) => println("found"); case _ => } 

Znaleziono

Jeśli naprawdę chcesz połączyć wyrażenie regularne, musisz to zrobić na poziomie ciągu znaków. Możesz pobrać java Pattern z regex Scala z .pattern, a następnie .pattern następnie dostaje ciąg. Większość Wyrażenia regularne mogą być pakowane bezpiecznie w (?:) uzyskać blok non-przechwytywania, więc można połączyć tak:

val Both = ("(?:"+Dig.pattern.pattern+")|(?:"+Wsp.pattern.pattern+")").r 

Jednakże wszelkie przechwytywanie grupy wewnątrz będzie zarówno być reprezentowane, ale nie wykorzystane oddział będzie być null (nie dokładnie to dobry sposób, aby napisać idiomatyczne Scala, ale w każdym razie, to co używa Java):

scala> "2" match { case Both(d,w) => if (w!=null) println("white") else println(d) } 
2 

scala> " " match { case Both(d,w) => if (w!=null) println("white") else println(d) } 
white 
+0

Dzięki, wersja z Dig (_) | Wsp (_) tak naprawdę nie działa dla mojej aplikacji, to były tylko przykłady, w mojej aktualnej aplikacji, mam długą listę Regeksów, więc używanie kilku wzorców nie byłoby wydajne. Sądzę, że prawdopodobnie zrobię to z drugim rozwiązaniem, miałem tylko nadzieję, że jest coś mniej strasznego, podobnego do mojego przykładu z Ruby. – Lykos

+2

@Lykos - Zawsze możesz napisać metodę, która łączy pary lub sekwencje. Na przykład jeśli 'xs' jest zbiorem wyrażeń regularnych,' xs.map (_. Pattern.pattern) .mkString ("(?:", ") | (?:", ")") .r' powinno być wyliczenie całej ich grupy razem –

+0

Tak, dziękuję, właściwie to właśnie zrobiłem. ^^ – Lykos

1

Jeśli chcesz połączyć i ponownego użycia części regex pisałem REL biblioteka/DSL, który nie tylko że. Przykład użycia dla ciebie sprawy:

import fr.splayce.rel._ 
import Implicits._ 

val a: RE = "\\d+" 
val b: RE = "\\s+" 
val c: RE = a | b 

c ma r sposób, aby uzyskać regex obiekt. Jest również w wersji Implicits, więc można go używać jako wyrażeń regularnych, na przykład c findAllIn someText. Jeśli zajdzie taka potrzeba, automatycznie zapakuje a i b w grupy niezapisujące.

Jeśli masz kolekcję regexes, można po prostu zrobić reduceLeft:

val regexes: List[RE] = List("a", "b", "c") 
regexes.reduceLeft(_ | _) 

Na marginesie:

  • jeśli importować Symbols._, masz krótkie notacje dla rzeczy jak \d i \s
  • implementuje większość zwykłych operacji regex w celu uzyskania maksymalnej przydatności do użycia

Zatem z REL, można napisać pierwszy przykład bezpośrednio jako:

val c = δ.+ | σ.+ 

Zapewnia również sposoby ponownego użycia i łączą związane wyciągami.

Jeśli wolisz waniliową scala, to nie mam nic do dodania do odpowiedzi Rexa Kerra.

+0

Dzięki, twoja biblioteka wygląda świetnie. Zawsze jednak niechętnie dodaję nową zależność do innej biblioteki do mojego projektu, jeśli używam go tylko w jednym miejscu, więc stosuję inne podejście. – Lykos

+1

Rozumiem, to też zrobiłbym na twoim stanowisku. Głównie pozostawiam tę odpowiedź innym czytelnikom, którzy mogą potrzebować bardziej kompletnego rozwiązania. –

1

Podczas gdy te odpowiedzi mogą działać, mogą być nieco przestarzałe lub skomplikowane.

val reg1 = "\\d+".r 
val reg2 = "\\s+".r 
val reg3 = s"${reg1}{$reg2}".r 
"123 " match { 
    case reg3(_*) => println("match") 
    case _ => println("no match") 
} 
+0

Dziękujemy za aktualizację. :) – Lykos

+0

Twój kod nie odpowiada na bieżące pytanie: "Chcę tylko wiedzieć, czy ciąg pasuje do jednej z listy podanych Regeksów" – akauppi

0

@akauppi jeśli chcesz listę regexes dopasować dany ciąg można zrobić coś takiego:

val regexes = List("\\d+".r, "\\s+".r, "a".r) 
val single = s"(${regexes.mkString("|")})".r 
"123" match { 
    case single(_*) = println("match") 
    case _ => println("no match") 
} 
// above prints: match 

"123 " match { 
    case single(_*) = println("match") 
    case _ => println("no match") 
} 
// above prints: no match 

Najlepszym sposobem wykorzystania listę regexes jest użycie notacji regex. To naprawdę jest to samo, co powiedzenie: