2010-07-23 9 views
128

Estoy un poco confundido sobre cuál es la diferencia entre un "grupo" y una "captura" cuando se trata del lenguaje de expresiones regulares de .NET. Considere el siguiente código C#:¿Cuál es la diferencia entre "grupos" y "capturas" en expresiones regulares de .NET?

MatchCollection matches = Regex.Matches("{Q}", @"^\{([A-Z])\}$"); 

espero que esto resulta en una sola captura por la letra 'Q', pero si puedo imprimir las propiedades del devuelta MatchCollection, veo:

matches.Count: 1 
matches[0].Value: {Q} 
     matches[0].Captures.Count: 1 
       matches[0].Captures[0].Value: {Q} 
     matches[0].Groups.Count: 2 
       matches[0].Groups[0].Value: {Q} 
       matches[0].Groups[0].Captures.Count: 1 
         matches[0].Groups[0].Captures[0].Value: {Q} 
       matches[0].Groups[1].Value: Q 
       matches[0].Groups[1].Captures.Count: 1 
         matches[0].Groups[1].Captures[0].Value: Q 

Qué está pasando aquí? Entiendo que también hay una captura para todo el partido, pero ¿cómo entran los grupos? ¿Y por qué matches[0].Captures no incluye la captura de la letra 'Q'?

Respuesta

94

No serás el primero en ser confuso. Esto es lo que el famoso Jeffrey Friedl tiene que decir al respecto (páginas 437+):

Dependiendo de su punto de vista, ya sea añade una nueva dimensión interesante para los resultados de los partidos , o añade confusión y hinchazón.

Y más adelante:

La principal diferencia entre un objeto Grupo y un objeto de captura es que cada objeto de grupo contiene una colección de capturas que representa todos los intermediarias partidos por la grupo durante el partido, así como el texto final que coincide con el grupo.

Y unas páginas más adelante, este es su conclusión:

Después de conseguir más allá de la documentación de .NET y en realidad entender lo que se suman estos objetos, Tengo sentimientos encontrados sobre ellos. En por una parte, es una innovación interesante [..] por otro lado, parece agregar una carga de eficiencia [..] de una funcionalidad que no se utilizará en la mayoría de los casos

En otras palabras: son muy similares, pero de vez en cuando y como sucede, encontrará un uso para ellos. Antes de hacer crecer otra barba gris, usted puede incluso conseguir aficionado a las capturas ...


Dado que ni el anterior, ni lo que se dice en el otro post que realmente parece responder a su pregunta, tenga en cuenta lo siguiente. Piensa en Captures como una especie de rastreador de historia. Cuando la expresión regular hace su partido, que pasa a través de la cadena de izquierda a derecha (haciendo caso omiso de dar marcha atrás por un momento) y cuando encuentra una coincidencia de captura de paréntesis, almacenará que en $x (x es cualquier dígito), digamos que $1.

Los motores de expresiones regulares normales, cuando los paréntesis de captura se van a repetir, arrojarán el actual $1 y lo reemplazarán con el nuevo valor. No .NET, que mantendrá este historial y lo ubicará en Captures[0].

Si cambiamos su expresión regular para buscar la siguiente manera:

MatchCollection matches = Regex.Matches("{Q}{R}{S}", @"(\{[A-Z]\})+"); 

se dará cuenta de que el primer Group tendrá una Captures (el primer grupo siempre siendo el todo partido, es decir, igual a $0) y la el segundo grupo tendrá {S}, es decir, solo el último grupo coincidente. Sin embargo, y aquí está el truco, si quiere encontrar las otras dos capturas, están en Captures, que contiene todas las capturas intermedias para {Q}{R} y {S}.

Si alguna vez se preguntó cómo podría obtener de la captura múltiple, que solo muestra la última coincidencia con las capturas individuales que están claramente allí en la cadena, debe usar Captures.

Una última palabra sobre su última pregunta: la coincidencia total siempre tiene una captura total, no la mezcle con los grupos individuales. Las capturas solo son interesantes dentro de los grupos.

+1

¡Gracias, el ejemplo concreto lo aclara! –

+0

'una funcionalidad que no se usará en la mayoría de los casos' Creo que se perdió el barco. En el corto plazo '(?:. *? (Información de la colección)) {4,20}' aumenta la eficiencia en más de un cientos de por ciento. – sln

+0

@sln, no estoy seguro de a qué se refiere y quién es "él" (¿frito?). El ejemplo que das parece no estar relacionado con esta discusión, o con las expresiones usadas. Además, los cuantificadores no codiciosos rara vez son más eficientes que los cuantificadores codiciosos, y requieren el conocimiento del conjunto de entrada y las pruebas de perfusión cuidadosas. – Abel

14

Desde el MSDN documentation:

La utilidad real de la propiedad Captura ocurre cuando un cuantificador se aplica a un grupo de captura de modo que el grupo de captura múltiples subcadenas en una sola expresión regular. En este caso, el objeto Grupo contiene información sobre la última subcadena capturada, mientras que la propiedad Capturas contiene información sobre todas las subcadenas capturadas por el grupo. En el siguiente ejemplo, la expresión regular \ b (\ w + \ s *) +. coincide con una oración completa que termina en un punto. El grupo (\ w + \ s *) + captura las palabras individuales en la colección. Como la colección de grupo contiene información solo sobre la última subcadena capturada, captura la última palabra de la oración, "oración". Sin embargo, cada palabra capturada por el grupo está disponible en la colección devuelta por la propiedad Capturas.

14

un grupo es lo que hemos asociado a los grupos en las expresiones regulares

"(a[zx](b?))" 

Applied to "axb" returns an array of 3 groups: 

group 0: axb, the entire match. 
group 1: axb, the first group matched. 
group 2: b, the second group matched. 

excepto que estos grupos son sólo 'capturados'. Los grupos no captura (utilizando el '(?:' sintaxis no están representados aquí

"(a[zx](?:b?))" 

Applied to "axb" returns an array of 2 groups: 

group 0: axb, the entire match. 
group 1: axb, the first group matched. 

una captura es también lo que nos hemos asociado con 'grupos capturados' Pero cuando el grupo se aplica con un cuantificador varias veces,.. Sólo el último partido se mantiene como el partido del grupo las capturas de matriz almacena todos estos partidos

"(a[zx]\s+)+" 

Applied to "ax az ax" returns an array of 2 captures of the second group. 

group 1, capture 0 "ax " 
group 1, capture 1 "az " 

en cuanto a su última pregunta -.. yo hubiera pensado antes de mirar a este que captura sería un arreglo de la capturas ordenadas por el grupo al que pertenecen. Más bien es solo un alias para los grupos [0]. Capturas. Bastante inútil.

2

Imagine que tiene la siguiente introducción de texto dogcatcatcat y un patrón como dog(cat(catcat))

En este caso, usted tiene 3 grupos, el primero (grupo importante) corresponde al partido.

Partido == == dogcatcatcat y Grupo0 dogcatcatcat

Grupo 1 == catcatcat

Group2 == catcat

Entonces, ¿qué es todo esto?

Consideremos un pequeño ejemplo escrito en C# (.NET) usando la clase Regex.

int matchIndex = 0; 
int groupIndex = 0; 
int captureIndex = 0; 

foreach (Match match in Regex.Matches(
     "dogcatabcdefghidogcatkjlmnopqr", // input 
     @"(dog(cat(...)(...)(...)))") // pattern 
) 
{ 
    Console.Out.WriteLine($"match{matchIndex++} = {match}"); 

    foreach (Group @group in match.Groups) 
    { 
     Console.Out.WriteLine($"\tgroup{groupIndex++} = {@group}"); 

     foreach (Capture capture in @group.Captures) 
     { 
      Console.Out.WriteLine($"\t\tcapture{captureIndex++} = {capture}"); 
     } 

     captureIndex = 0; 
    } 

    groupIndex = 0; 
    Console.Out.WriteLine(); 
     } 

salida:

match0 = dogcatabcdefghi 
    group0 = dogcatabcdefghi 
     capture0 = dogcatabcdefghi 
    group1 = dogcatabcdefghi 
     capture0 = dogcatabcdefghi 
    group2 = catabcdefghi 
     capture0 = catabcdefghi 
    group3 = abc 
     capture0 = abc 
    group4 = def 
     capture0 = def 
    group5 = ghi 
     capture0 = ghi 

match1 = dogcatkjlmnopqr 
    group0 = dogcatkjlmnopqr 
     capture0 = dogcatkjlmnopqr 
    group1 = dogcatkjlmnopqr 
     capture0 = dogcatkjlmnopqr 
    group2 = catkjlmnopqr 
     capture0 = catkjlmnopqr 
    group3 = kjl 
     capture0 = kjl 
    group4 = mno 
     capture0 = mno 
    group5 = pqr 
     capture0 = pqr 

Vamos a analizar sólo el primer partido (match0).

Como se puede ver hay tres grupos de menores : group3, group4 y group5

group3 = kjl 
     capture0 = kjl 
    group4 = mno 
     capture0 = mno 
    group5 = pqr 
     capture0 = pqr 

Esos grupos (3-5) fueron creados debido a la 'sub-patrón ' (...)(...)(...) del principal patrón(dog(cat(...)(...)(...)))

El valor de group3 corresponde a su captura (capture0). (Como en el caso de group4 y group5). Eso es porque no hay repetición de grupo como (...){3}.


Ok, vamos a considerar otro ejemplo, donde hay una repetición grupo.

En caso de modificar el patrón de expresión regular para buscar coincidencias (por código de la imagen arriba) (dog(cat(...)(...)(...)))-(dog(cat(...){3})), se dará cuenta de que existe la siguiente repetición grupo: (...){3}.

Ahora el salida ha cambiado:

match0 = dogcatabcdefghi 
    group0 = dogcatabcdefghi 
     capture0 = dogcatabcdefghi 
    group1 = dogcatabcdefghi 
     capture0 = dogcatabcdefghi 
    group2 = catabcdefghi 
     capture0 = catabcdefghi 
    group3 = ghi 
     capture0 = abc 
     capture1 = def 
     capture2 = ghi 

match1 = dogcatkjlmnopqr 
    group0 = dogcatkjlmnopqr 
     capture0 = dogcatkjlmnopqr 
    group1 = dogcatkjlmnopqr 
     capture0 = dogcatkjlmnopqr 
    group2 = catkjlmnopqr 
     capture0 = catkjlmnopqr 
    group3 = pqr 
     capture0 = kjl 
     capture1 = mno 
     capture2 = pqr 

Una vez más, vamos a analizar sólo el primer partido (match0).

No hay más grupos menores group4 y group5 debido (...){3}repetición ({n} en donden> = 2) que han sido fusionados en un solo grupo group3.

En este caso, el valor corresponde a group3 es capture2 (la última captura, en otras palabras).

Por lo tanto, si necesita todas las 3 capturas interiores (capture0, capture1, capture2) que tendrá que desplazarse a través de la colección del grupo Captures.

La ubicación es: preste atención a la forma en que diseña los grupos de su patrón. Usted debe pensar por adelantado qué comportamiento hace que la especificación del grupo, como (...)(...), (...){2} o (.{3}){2} etc.


Es de esperar que ayudará a arrojar algo de luz sobre las diferencias entre Captura, Grupos y Partidos como bien.

4

Esto se puede explicar con un simple ejemplo (e imágenes).

Coincidencia 3:10pm con la expresión regular ((\d)+):((\d)+)(am|pm), y el uso de Mono interactiva csharp:

csharp> Regex.Match("3:10pm", @"((\d)+):((\d)+)(am|pm)"). 
     > Groups.Cast<Group>(). 
     > Zip(Enumerable.Range(0, int.MaxValue), (g, n) => "[" + n + "] " + g); 
{ "[0] 3:10pm", "[1] 3", "[2] 3", "[3] 10", "[4] 0", "[5] pm" } 

Entonces, ¿dónde está el 1? enter image description here

Puesto que hay varios dígitos que coinciden en el cuarto grupo, sólo "llegar a" el último partido si hacemos referencia al grupo (con una implícita ToString(), se entiende). Con el fin de exponer los partidos intermedios, tenemos que ir más profundo y hacer referencia a la propiedad Captures en el grupo en cuestión:

csharp> Regex.Match("3:10pm", @"((\d)+):((\d)+)(am|pm)"). 
     > Groups.Cast<Group>(). 
     > Skip(4).First().Captures.Cast<Capture>(). 
     > Zip(Enumerable.Range(0, int.MaxValue), (c, n) => "["+n+"] " + c); 
{ "[0] 1", "[1] 0" } 

enter image description here

Cortesía de this article.

Cuestiones relacionadas