2009-11-06 11 views
7

Ich versuche, Teile einer mehrzeiligen Zeichenfolge mit einem Regex in Scala zu erfassen. Der Eingang ist von der Form:Scala Regex Mehrere Block Capturing

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

ich mehrere Möglichkeiten ausprobiert habe, die mir den Text aus den begin {} Blöcke bekommen sollte. Einer von ihnen:

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

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

Ich bekomme eine NO MATCH. Wenn ich die \} fallen lasse, sieht die Regex wie (?s).*begin \{(.*) aus und es entspricht dem letzten Block einschließlich der unerwünschten } und "etwas Text". Ich habe meine Regex bei rugular.com wie mit /.*begin \{(.*)\}/m überprüft und es entspricht mindestens einem Block. Ich dachte, wenn meine Scala-Regex dasselbe würde, könnte ich anfangen, findAllIn zu verwenden, um alle Blöcke zu vergleichen. Was mache ich falsch?

Ich hatte einen Blick auf Scala Regex enable Multiline option, aber ich konnte nicht alle Vorkommen der Textblöcke in zum Beispiel eine Seq[String] erfassen. Jede Hilfe wird geschätzt.

Antwort

10

Alex Wie gesagt, wenn Musteranpassung unter Verwendung von Feldern von regulären Ausdrücken zu extrahieren, das Muster wirkt, als wäre sie begrenzt wurde (dh unter Verwendung ^ und $). Der übliche Weg, um dieses Problem zu vermeiden, ist die Verwendung von findAllIn zuerst. Auf diese Weise:

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") 
}) 

Andernfalls können Sie .* am Anfang verwenden und enden um diese Einschränkung zu erhalten:

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

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

By the way, haben Sie wahrscheinlich eine nicht-eifrig Matcher wollen:

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

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

Wissen Sie, ob das irgendwo dokumentiert ist? –

+1

Danke Daniel für deine ausführliche Antwort. Klappt wunderbar. –

+0

Alex, an diesem Punkt bin ich mir nicht sicher. Ich habe so viel mit Regex gemacht und sogar die Bibliothek erweitert, dass ich mich nicht mal daran erinnern kann, was die Bibliothek bietet oder nicht! Zum Beispiel würde ich 'Block findAllMatchesIn input map (_ group 0)' schreiben, als ich entdeckte, dass diese Methode in der Bibliothek nicht existiert wie sie ist. –

1

Wenn ich ein Spiel mache, glaube ich, dass eine vollständige Übereinstimmung erforderlich ist. Ihr Spiel ist äquivalent zu:

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

Es funktioniert, wenn Sie ein * bis zum Ende.

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

Ich habe nicht in der Lage gewesen, eine Dokumentation zu diesem Thema zu finden, aber ich habe das gleiche angetroffen Problem.

+0

Yup, dass die Arbeit getan, danke. –

0

Als Ergänzung zu den anderen Antworten wollte ich auf die Existenz von kantan.regex hinweisen, mit dem Sie Folgendes schreiben können:

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 

Dies ergibt:

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