2009-11-06 8 views
7

Estoy tratando de capturar partes de una cadena con múltiples líneas con una expresión regular en Scala. La entrada es de la forma:Scala Regex Captura de múltiples bloques

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

He intentado varias posibilidades que me deben recibir el texto de los bloques begin {}. Uno de ellos:

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

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

obtengo un NO MATCH. Si dejo caer el \}, la expresión regular se ve como (?s).*begin \{(.*) y coincide con el último bloque, incluido el } no deseado y "algún texto". Comprobé mi expresión regular en rubular.com como con /.*begin \{(.*)\}/m y coincide con al menos un bloque. Pensé que cuando mi Scala Regex coincidiría lo mismo podría comenzar a usar findAllIn para que coincida con todos los bloques. ¿Qué estoy haciendo mal?

He echado un vistazo a Scala Regex enable Multiline option pero no he podido capturar todas las apariciones de los bloques de texto en, por ejemplo, un Seq[String]. Se agradece cualquier ayuda.

Respuesta

10

Como Alex ha dicho, cuando se utiliza la coincidencia de patrones para extraer campos de expresiones regulares, el patrón actúa como si estaba limitada (es decir, usando ^ y $). La forma habitual de evitar este problema es usar primero findAllIn. De esta manera:

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

De lo contrario, puede utilizar .* al comienzo y al final de moverse por esa restricción:

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

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

Por cierto, es probable que desee un matcher no ansiosa:

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

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

¿Sabe si esto está documentado en alguna parte? –

+1

Gracias a Daniel por su respuesta detallada. Funciona de maravilla. –

+0

Alex, en este punto, no estoy seguro. ¡Hice tanto con Regex, incluso extendiendo la biblioteca, que ni siquiera puedo recordar qué proporciona o no la biblioteca! Por ejemplo, iba a escribir 'Bloquear el mapa de entrada findAllMatchesIn (_ grupo 0)', cuando descubrí que este método no existe en la biblioteca tal como está. –

1

Al hacer una coincidencia, creo que se requiere una coincidencia completa. Su partido es equivalente a:

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

Funciona si se agrega * hasta el final:.

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

No he podido encontrar ninguna documentación sobre este tema, pero me he encontrado con esta misma problema.

+0

Yup, que hizo el trabajo, gracias. –

0

Como complemento de las otras respuestas, quería señalar la existencia de kantan.regex, que le permite escribir lo siguiente:

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 

Esto produce:

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