regex - que - expresiones regulares ejemplos
F#Mapeando coincidencias de expresiones regulares con patrones activos (1)
Cosas interesantes, creo que todo lo que estás explorando aquí es válido. De hecho, los patrones activos (parciales) para la coincidencia de expresiones regulares funcionan muy bien. Especialmente cuando tiene una cadena que desea comparar con varios casos alternativos. Lo único que sugeriría con los patrones activos de expresiones regulares más complejos es que les dé nombres más descriptivos, posiblemente creando una colección de diferentes patrones de expresiones regulares con diferentes propósitos.
En cuanto al ejemplo de C # a F #, puede tener una solución funcional bien sin patrones activos, por ejemplo
let testString = "http://www.bob.com http://www.b.com http://www.bob.com http://www.bill.com"
let matches input =
Regex.Matches(input, "(http://///S+)")
|> Seq.cast<Match>
|> Seq.groupBy (fun m -> m.Value)
|> Seq.map (fun (value, groups) -> value, (groups |> Seq.length))
//FSI output:
> matches testString;;
val it : seq<string * int> =
seq
[("http://www.bob.com", 2); ("http://www.b.com", 1);
("http://www.bill.com", 1)]
Actualizar
La razón por la que este ejemplo en particular funciona bien sin patrones activos es porque 1) solo está probando un patrón, 2) está procesando dinámicamente las coincidencias.
Para un ejemplo real de patrones activos, consideremos un caso en el que 1) estamos probando múltiples expresiones regulares, 2) estamos probando una coincidencia de expresiones regulares con múltiples grupos. Para estos escenarios, uso los siguientes dos patrones activos, que son un poco más generales que el primer patrón activo de Match
que mostraste (no descarto el primer grupo en la coincidencia y devuelvo una lista de los objetos del grupo, no solo sus valores: uno usa la opción de expresión regular compilada para patrones de expresiones regulares estáticas, uno usa la opción de expresión regular interpretada para patrones de expresión regular dinámica. Debido a que la API de expresiones regulares .NET está tan llena de características, lo que devuelve de su patrón activo es realmente lo que le resulte útil. Pero devolver una list
de algo es bueno, porque entonces puedes hacer un patrón de coincidencia en esa lista.
let (|InterpretedMatch|_|) pattern input =
if input = null then None
else
let m = Regex.Match(input, pattern)
if m.Success then Some [for x in m.Groups -> x]
else None
///Match the pattern using a cached compiled Regex
let (|CompiledMatch|_|) pattern input =
if input = null then None
else
let m = Regex.Match(input, pattern, RegexOptions.Compiled)
if m.Success then Some [for x in m.Groups -> x]
else None
Observe también cómo estos patrones activos consideran nulos una no coincidencia, en lugar de lanzar una excepción.
OK, entonces digamos que queremos analizar nombres. Tenemos los siguientes requisitos:
- Debe tener nombre y apellido
- Puede tener segundo nombre
- Primero, el medio y el apellido opcionales están separados por un solo espacio en blanco en ese orden
- Cada parte del nombre puede consistir en cualquier combinación de al menos una o más letras o números
- La entrada puede estar mal formada
Primero definiremos el siguiente registro:
type Name = {First:string; Middle:option<string>; Last:string}
Luego podemos usar nuestro patrón de expresión regular de manera bastante efectiva en una función para analizar un nombre:
let parseName name =
match name with
| CompiledMatch @"^(/w+) (/w+) (/w+)$" [_; first; middle; last] ->
Some({First=first.Value; Middle=Some(middle.Value); Last=last.Value})
| CompiledMatch @"^(/w+) (/w+)$" [_; first; last] ->
Some({First=first.Value; Middle=None; Last=last.Value})
| _ ->
None
Tenga en cuenta que una de las ventajas clave que obtenemos aquí, que es el caso de la coincidencia de patrones en general, es que podemos probar simultáneamente que una entrada coincide con el patrón de expresiones regulares, y descomponer la lista de grupos devueltos si lo hace.
Encontré este artículo útil sobre el uso de patrones activos con expresiones regulares: http://www.markhneedham.com/blog/2009/05/10/f-regular-expressionsactive-patterns/
El fragmento de código original utilizado en el artículo fue este:
open System.Text.RegularExpressions
let (|Match|_|) pattern input =
let m = Regex.Match(input, pattern) in
if m.Success then Some (List.tl [ for g in m.Groups -> g.Value ]) else None
let ContainsUrl value =
match value with
| Match "(http://///S+)" result -> Some(result.Head)
| _ -> None
Lo que le permitiría saber si se encontró al menos una url y cuál fue esa url (si entendí el fragmento correctamente)
Luego, en la sección de comentarios, Joel sugirió esta modificación:
Alternativa, ya que un grupo dado puede o no ser una coincidencia exitosa:
List.tail [ for g in m.Groups -> if g.Success then Some g.Value else None ]
O quizás le das etiquetas a tus grupos y quieres acceder a ellos por su nombre:
(re.GetGroupNames() |> Seq.map (fun n -> (n, m.Groups.[n])) |> Seq.filter (fun (n, g) -> g.Success) |> Seq.map (fun (n, g) -> (n, g.Value)) |> Map.ofSeq)
Después de intentar combinar todo esto, se me ocurrió el siguiente código:
let testString = "http://www.bob.com http://www.b.com http://www.bob.com http://www.bill.com"
let (|Match|_|) pattern input =
let re = new Regex(pattern)
let m = re.Match(input) in
if m.Success then Some ((re.GetGroupNames()
|> Seq.map (fun n -> (n, m.Groups.[n]))
|> Seq.filter (fun (n, g) -> g.Success)
|> Seq.map (fun (n, g) -> (n, g.Value))
|> Map.ofSeq)) else None
let GroupMatches stringToSearch =
match stringToSearch with
| Match "(http://///S+)" result -> printfn "%A" result
| _ -> ()
GroupMatches testString;;
Cuando ejecuto mi código en una sesión interactiva, esto es lo que se genera:
map [("0", "http://www.bob.com"); ("1", "http://www.bob.com")]
El resultado que estoy tratando de lograr se vería algo así:
map [("http://www.bob.com", 2); ("http://www.b.com", 1); ("http://www.bill.com", 1);]
Básicamente, una asignación de cada coincidencia única encontrada seguida por el conteo de la cantidad de veces que se encontró una cadena coincidente específica en el texto.
Si cree que voy por el camino equivocado aquí, no dude en sugerir un enfoque completamente diferente. Soy un tanto nuevo tanto para los patrones activos como para las expresiones regulares, por lo que no tengo ni idea de por dónde empezar a tratar de solucionar este problema.
También se me ocurrió esto, que es básicamente lo que haría en C # traducido a F #.
let testString = "http://www.bob.com http://www.b.com http://www.bob.com http://www.bill.com"
let matches =
let matchDictionary = new Dictionary<string,int>()
for mtch in (Regex.Matches(testString, "(http://///S+)")) do
for m in mtch.Captures do
if(matchDictionary.ContainsKey(m.Value)) then
matchDictionary.Item(m.Value) <- matchDictionary.Item(m.Value) + 1
else
matchDictionary.Add(m.Value, 1)
matchDictionary
Lo que devuelve esto cuando se ejecuta:
val matches : Dictionary = dict [("http://www.bob.com", 2); ("http://www.b.com", 1); ("http://www.bill.com", 1)]
Este es básicamente el resultado que estoy buscando, pero estoy tratando de aprender la forma funcional de hacerlo, y creo que debería incluir patrones activos. No dude en intentar "funcionalizar" esto si tiene más sentido que mi primer intento.
Gracias por adelantado,
Mover