2009-11-06 16 views
7

Próbuję uchwycić części wielo-liniowy ciąg z regex w Scala. wejściowy ma postać:Scala Regex wielokrotnego blokowania przechwytywanie

val input = """some text 
       |begin { 
       | content to extract 
       | content to extract 
       |} 
       |some text 
       |begin { 
       | other content to extract 
       |} 
       |some text""".stripMargin 

Próbowałem kilka możliwości, które powinny zrozumcie mnie tekst spośród begin {} bloków. Jeden z nich:

val Block = """(?s).*begin \{(.*)\}""".r 

input match { 
    case Block(content) => println(content) 
    case _ => println("NO MATCH") 
} 

Dostaję NO MATCH. Jeśli upuszczę \}, regex wygląda jak (?s).*begin \{(.*) i pasuje do ostatniego bloku zawierającego niechciany } i "jakiś tekst". Sprawdziłem moje wyrażenie na rubular.com jak w przypadku /.*begin \{(.*)\}/m i pasuje co najmniej jeden blok. Pomyślałem, że gdy moje regex Scala będzie pasować tak samo, mogę zacząć używać findAllIn, aby dopasować wszystkie bloki. Co ja robię źle?

Spojrzałem na Scala Regex enable Multiline option, ale nie udało mi się przechwycić wszystkich wystąpień bloków tekstu, na przykład Seq[String]. Każda pomoc jest doceniana.

Odpowiedz

10

Jak Alex powiedział podczas korzystania pasujące do wzorca, aby wyodrębnić pól z wyrażeń regularnych, wzór działa tak, jakby była ona ograniczona (czyli używając ^ i $). Najczęstszym sposobem uniknięcia tego problemu jest najpierw użycie findAllIn. W ten sposób:

val input = """some text 
       |begin { 
       | content to extract 
       | content to extract 
       |} 
       |some text 
       |begin { 
       | other content to extract 
       |} 
       |some text""".stripMargin 

val Block = """(?s)begin \{(.*)\}""".r 

Block findAllIn input foreach (_ match { 
    case Block(content) => println(content) 
    case _ => println("NO MATCH") 
}) 

W przeciwnym razie, można użyć .* na początku i na końcu, aby ominąć tego ograniczenia:

val Block = """(?s).*begin \{(.*)\}.*""".r 

input match { 
    case Block(content) => println(content) 
    case _ => println("NO MATCH") 
} 

Nawiasem mówiąc, prawdopodobnie chcesz non-chętny dopasowywania:

val Block = """(?s)begin \{(.*?)\}""".r 

Block findAllIn input foreach (_ match { 
    case Block(content) => println(content) 
    case _ => println("NO MATCH") 
}) 
+0

Czy wiesz, że jest to udokumentowane w dowolnym miejscu? –

+1

Dziękuję Danielowi za szczegółową odpowiedź. Działa jak marzenie. –

+0

Alex, w tym momencie nie jestem pewien. Zrobiłem tak wiele z Regeksem, nawet rozszerzając bibliotekę, że nawet nie pamiętam, co zapewnia biblioteka, czy nie! Na przykład miałem zamiar napisać 'Block inputAllMatchesIn input map (_ group 0)', gdy odkryłem, że ta metoda nie istnieje w obecnej bibliotece. –

1

Uważam, że przy dopasowaniu wymagana jest pełna zgodność dopasowania. Twoja gra jest równoznaczne z:

val Block = """^(?s).*begin \{(.*)\}$""".r 

To działa, jeśli dodać * do końca.

val Block = """(?s).*begin \{(.*)\}.*""".r 

I nie były w stanie znaleźć żadnej dokumentacji na ten temat, ale ja spotkałem ten sam kwestia.

+0

Tak, że nie działa, dzięki. –

0

Jako uzupełnienie innych odpowiedzi, chciałem wskazać na istnienie kantan.regex, co pozwala napisać:

import kantan.regex.ops._ 

// The type parameter is the type as which to decode results, 
// the value parameters are the regular expression to apply and the group to 
// extract data from. 
input.evalRegex[String]("""(?s)begin \{(.*?)\}""", 1).toList 

Daje:

List(Success(
    content to extract 
    content to extract 
), Success(
    other content to extract 
))