2012-01-13 15 views
5

¿Hay alguna otra forma de escribir lo siguiente?¿Cómo puedo encontrar la primera expresión regular que coincida con mi entrada en una lista de expresiones regulares?

string input; 

var match = Regex.Match(input, @"Type1"); 

if (!match.Success) 
{ 
    match = Regex.Match(input, @"Type2"); 
} 

if (!match.Success) 
{ 
    match = Regex.Match(input, @"Type3"); 
} 

Básicamente, quiero ejecutar mi cadena a través de un gammut de expresiones y ver cuál se pega.

+4

Coloque las expresiones regulares en una lista y repita la lista hasta que llegue a una. – CodesInChaos

+0

'Poner las expresiones regulares en una lista, y luego iterar sobre esa lista hasta llegar a one.' o escribir una expresión regular mejor como' Tipo 1 | Tipo 2 | Type3' –

+0

@LB cómo averiguar qué parte de la expresión regular tuvo éxito cuando se combinan ¿ellos? Asumiendo que las expresiones regulares no son triviales. – CodesInChaos

Respuesta

0

Otra forma de hacerlo. Se repite en toda la lista, PERO puede buscar un número variable o cadenas para que coincidan sin tener que escribir x número de sentencias if.

string input = "Type1"; 
List<string> stringsToTest = new List<string>() { @"Type1", @"Type2", @"Type3" }; 

var q = from string t in stringsToTest 
     where Regex.IsMatch(input, t) 
     select t; 

//This way you can get how many strings on the list matched the input 
foreach(string s in q) 
{ 
    Console.WriteLine(s); 
} 
+0

El OP quiere saber cuál coincide, no solo eso coincide. – CodesInChaos

+0

Es cierto, acabo de cambiar. – H27studio

10
var patterns = new[] { "Type1", "Type2", "Type3" }; 
Match match; 
foreach (string pattern in patterns) 
{ 
    match = Regex.Match(input, pattern); 
    if (match.Success) 
     break; 
} 

o

var patterns = new[] { "Type1", "Type2", "Type3" }; 
var match = patterns 
    .Select(p => Regex.Match(input, p)) 
    .FirstOrDefault(m => m.Success); 

// In your original example, match will be the last match if all are 
// unsuccessful. I expect this is an accident, but if you want this 
// behavior, you can do this instead: 
var match = patterns 
    .Select(p => Regex.Match(input, p)) 
    .FirstOrDefault(m => m.Success) 
    ?? Regex.Match(input, patterns[patterns.Length - 1]); 

Debido a LINQ to Objects utiliza ejecución diferida, Regex.Match es llamado solo hasta que se encuentra una coincidencia, por lo que no tiene que preocuparse acerca de este enfoque es demasiado ansioso.

+2

Creo que necesitarás 'FirstOrDefault' ya que probablemente no esté garantizado que al menos una coincida. – CodesInChaos

+0

@CodeInChaos: Buena captura, editado. –

+3

También se podría crear una lista de expresiones regulares compiladas para un mejor rendimiento. – CodesInChaos

5

Sí, me gustaría escribir como este para evitar la ejecución de los partidos expresiones regulares varias veces:

 match = Regex.Match(input, @"Type1|Type2|Type3"); 

     if (match.Success) 
     { 
      // loop, in case you are matching to multiple occurrences within the input. 
      // However, Regex.Match(string, string) will only match to the first occurrence. 
      foreach (Capture capture in match.Captures) 
      { 
       // if you care to determine which one (Type1, Type2, or Type3) each capture is 
       switch (capture.Value) 
       { 
        case "Type1": 
         // ... 
         break; 
        case "Type2": 
         // ... 
         break; 
        case "Type3": 
         // ... 
         break; 
       } 
      } 
     } 

Alternativamente, si usted tiene una lista arbitraria de los patrones que desea comprobar:

 // assumption is that patterns contains a list of valid Regex expressions 
     match = Regex.Match(input, string.Join("|", patterns)); 

     if (match.Success) 
     { 
      // obviously, only one of these return statements is needed 

      // return the first occurrence 
      return match.Captures[0].Value; 

      // return an IEnumerable<string> of the matched patterns 
      return match.Captures.OfType<Capture>().Select(capture => capture.Value); 
     } 

Aquí hay otro enfoque que usa grupos de captura con nombre para indexar cada patrón. cuando se encuentra una coincidencia, intentamos determinar cuál de los grupos de captura se correspondió.

mucho me disgusta este código debido a la concatenación innecesaria repetida de "patrón" con el índice, pero no estoy seguro de cómo hacer esto más limpio:

EDIT: He limpiado este codifique un poco utilizando un diccionario

 // assumption is that patterns contains a list of valid Regex expressions 
     int i = 0; 
     var mapOfGroupNameToPattern = patterns.ToDictionary(pattern => "Pattern" + (i++)); 

     match = Regex.Match(input, string.Join("|", mapOfGroupNameToPattern.Select(x => "(?<" + x.Key + ">" + x.Value + ")"))); 

     if (match.Success) 
     { 
      foreach (var pattern in mapOfGroupNameToPattern) 
      { 
       if (match.Groups[pattern.Key].Captures.Count > 0) 
       { 
        // this is the pattern that was matched 
        return pattern.Value; 
       } 
      } 
     } 
+0

Su código solo funciona porque las expresiones regulares de muestra no contienen ningún comodín. Supongo que las expresiones reales reales no son tan triviales. – CodesInChaos

+0

@CodeInChaos - hmm, punto justo. Trataré de abordar esa posibilidad. –

+0

Si hay un problema con mi código o mi enfoque, agradecería opiniones. No afirmaré que este código sea excelente, pero hasta donde tengo conocimiento (y de mis propias pruebas) parece que hace el trabajo, y con solo una llamada a Regex.Match al respecto. Aunque no estoy seguro de si este problema se resuelve de manera más eficiente mediante la combinación de patrones en una sola expresión regular (como lo hacen mis ejemplos) o realizando múltiples coincidencias de expresiones regulares (como lo hacen las otras respuestas). Curioso si alguien tiene una opinión. –

Cuestiones relacionadas