2009-11-06 16 views
7

Sto provando a catturare parti di una stringa multistrato con un'espressione regolare in Scala. L'ingresso è nella forma:Scala Scala blocco multiplo Cattura

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

Ho provato diverse possibilità che mi dovrebbe ottenere il testo fuori dai begin {} blocchi. Uno di questi:

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

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

Ottengo un NO MATCH. Se lascio il \} il regex assomiglia a (?s).*begin \{(.*) e corrisponde all'ultimo blocco compreso lo } indesiderato e "alcun testo". Ho controllato il mio regex su rubular.com come con /.*begin \{(.*)\}/m e corrisponde almeno a un blocco. Ho pensato che quando il mio regex di Scala corrispondesse allo stesso modo, potrei iniziare a usare findAllIn per abbinare tutti i blocchi. Che cosa sto facendo di sbagliato?

Ho dato un'occhiata a Scala Regex enable Multiline option ma non sono riuscito a catturare tutte le occorrenze dei blocchi di testo, ad esempio, a Seq[String]. Qualsiasi aiuto è apprezzato.

risposta

10

Come Alex ha detto, quando si utilizza il pattern matching per estrarre i campi da espressioni regolari, il modello si comporta come se fosse stato limitato (vale a dire, usando ^ e $). Il solito modo per evitare questo problema è utilizzare prima findAllIn. In questo modo:

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

In caso contrario, è possibile utilizzare .* all'inizio e alla fine per aggirare tale restrizione:

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

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

Tra l'altro, probabilmente si desidera un matcher non ansiosi:

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

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

Sai se questo è documentato ovunque? –

+1

Grazie Daniel per la tua risposta dettagliata. Funziona come un fascino. –

+0

Alex, a questo punto, non ne sono sicuro. Ho fatto così tanto con Regex, anche estendendo la libreria, che non riesco nemmeno a ricordare cosa fornisce la libreria o no! Ad esempio, stavo per scrivere 'Block findAllMatchesIn input map (_ group 0)', quando ho scoperto che questo metodo non esiste nella libreria così com'è. –

1

Quando si esegue una corrispondenza, credo che sia necessaria una completa corrispondenza implicita. Il vostro incontro è equivalente a:

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

Funziona se si aggiunge * fino alla fine:.

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

non sono stato in grado di trovare alcuna documentazione su questo, ma ho incontrato questo stesso problema.

+0

Yup, che ha funzionato, grazie. –

0

Come complemento alle altre risposte, volevo sottolineare l'esistenza di kantan.regex, che consente di scrivere quanto segue:

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 

Questo produce:

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