2010-04-08 9 views
12

Digamos que tengo dos cadenas como esta:expresión regular: Coincidencia contra los grupos en orden diferente sin tener que repetir el grupo

XABY 
XBAY 

Una simple expresión regular que coincide tanto sería algo así:

X(AB|BA)Y 

Sin embargo , Tengo un caso donde A y B son cadenas complicadas, y estoy buscando una manera de evitar tener que especificar cada una de ellas dos veces (en cada lado de |). ¿Hay alguna manera de hacer esto (que presumiblemente es más simple que tener que especificarlos dos veces)?

Gracias

Respuesta

18
X(?:A()|B()){2}\1\2Y 

Básicamente, se utiliza un grupo de captura vacía para marcar cada elemento cuando está emparejado, a continuación, las copias de las referencias asegurarse de que todo ha sido verificado fuera.

Tenga en cuenta que esto se basa en el comportamiento de expresiones regulares indocumentado, por lo que no hay garantía de que va a trabajar en su sabor expresiones regulares - y si lo hace, no hay garantía de que va a continuar a trabajar a medida que evoluciona de sabor. Pero hasta donde sé, funciona en todos los sabores que admiten referencias. (EDIT: no funciona en JavaScript.)

EDIT: Usted dice que está usando grupos nombrados para capturar partes del partido, lo que añade una gran cantidad de desorden visual para la expresión regular, si no la complejidad real. Bueno, si está usando expresiones regulares de .NET, aún puede usar grupos numerados simples para las "casillas de verificación". He aquí un ejemplo simplista que encuentra y recoge además un montón de cuerdas meses en día sin conocer su orden interno:

Regex r = new Regex(
    @"(?: 
     (?<MONTH>Jan|Feb|Mar|Apr|May|Jun|Jul|Sep|Oct|Nov|Dec)() 
     | 
     (?<DAY>\d+)() 
    ){2} 
     \1\2", 
    RegexOptions.IgnorePatternWhitespace); 

    string input = @"30Jan Feb12 Mar23 4Apr May09 11Jun"; 
    foreach (Match m in r.Matches(input)) 
    { 
    Console.WriteLine("{0} {1}", m.Groups["MONTH"], m.Groups["DAY"]); 
    } 

Esto funciona porque en .NET, la presencia de grupos nombrados no tiene efecto en el ordenamiento de la no grupos con nombre Los grupos con nombre tienen números asignados, pero esos números comienzan en después de el último de los grupos sin nombre. (Sé que parece innecesariamente complicado, pero hay buenas razones para hacerlo de esa manera.)

Normalmente, debe evitar el uso de grupos de captura nombrados y no nombrados, especialmente si utiliza referencias anteriores, pero Creo que este caso podría ser una excepción legítima.

+0

¡Funciona! Como mis expresiones A y B contienen un grupo completo de grupos propios, utilicé grupos de captura vacíos con nombre: X (?: A (? ) | B (? )) {2} \ k \ k Y – Jimmy

+0

@Jimmy: ver mi edición sobre los grupos nombrados. –

+0

Gracias por la sugerencia adicional. Estoy usando .net, pero mis cadenas A y B también contienen un grupo de grupos de captura sin nombre (cuando creo la expresión regular, uso RegexOptions.ExplicitCapture). Creo que usar?: En todos estos grupos crea más desorden que usar dos grupos denominados 'ficticios'. Comentarios adicionales bienvenidos :) – Jimmy

1

Si hay varias cadenas, con cualquier tipo de caracteres de allí, usted estará mejor con:

X(.)+Y 

Sólo los números entonces sólo

X([0-9])+Y 

letras

X([a-zA-Z])+Y 

letras y los números

X([a-zA-Z][0-9])+Y 
+0

n, A y B debe coincidir con mayor precisión que eso; por ejemplo, la parte 'A' que uso en mi ejemplo es en realidad (s = \ s * (? \ d * \.? \ d *) \ s + y la parte 'B' en realidad es r = \ s * (? \ d) (/ (? \ d))?). Es por eso que AB | AB se vuelve difícil de mantener. – Jimmy

3

Puede almacenar piezas de expresiones regulares en las variables y hacer:

A=/* relevant regex pattern */ 
B=/* other regex pattern */ 
regex = X($A$B|$B$A)Y 

De esta manera sólo tiene que especificar cada expresión regular una vez, en su propia línea, lo que debería hacer que sea más fácil de mantener.

Sidenote: Estás tratando de encontrar permutaciones, lo cual está bien ya que solo estás viendo 2 subrepresos. Pero si desea agregar un tercero (o cuarto), sus permutaciones de expresiones regulares crecen drásticamente (abc | acb | bac | bca | cab | cba) - o algo peor. Si necesita ir por el camino de las permutaciones, hay una buena discusión al respecto aquí en stackoverflow. Es para permutación de letras, y las soluciones usan awk/bash/perl, pero eso al menos te da un punto de partida.

+0

Disculpe, esto no es demasiado útil. Utilizo un editor de expresiones regulares para probar la expresión regular, y esa es la parte que se vuelve difícil de manejar cuando repito expresiones regulares complejas ... el almacenamiento de la expresión regular en partes de mi programa no ayudaría en este caso. – Jimmy

1

prueba este

X((A|B){2})Y 
+1

Lo siento, eso no funcionaría - No quiero permitir cadenas como AA o BB - solo AB o BA :) – Jimmy

Cuestiones relacionadas