2010-03-07 17 views
10

pensé que por defecto mi Regex exhibiría el comportamiento codicioso que quiero, pero no lo es en el siguiente código:expresiones regulares se comporta perezoso, debe ser codicioso

Regex keywords = new Regex(@"in|int|into|internal|interface"); 
var targets = keywords.ToString().Split('|'); 
foreach (string t in targets) 
    { 
    Match match = keywords.Match(t); 
    Console.WriteLine("Matched {0,-9} with {1}", t, match.Value); 
    } 

Salida:

Matched in  with in 
Matched int  with in 
Matched into  with in 
Matched internal with in 
Matched interface with in 

Ahora se dan cuenta de que podía conseguir que funcione para este pequeño ejemplo si simplemente solucionaron las palabras clave por longitud descendente, pero

  • quiero entender por qué esto no está funcionando como se esperaba, y
  • el proyecto actual que estoy trabajando en tiene muchas más palabras en la expresión regular y es importante mantenerlos en orden alfabético.

Así que mi pregunta es: ¿Por qué esto es perezoso y cómo lo soluciono?

+0

No estoy seguro de si su uso real es más complicado, pero si el ejemplo anterior es en realidad lo que está haciendo, creo que sería mil veces mejor ir por su lista de palabras buscando coincidencias con el método IndexOf . Si la expresión regular simplemente contiene un montón de palabras en una alternancia, es probable que el rendimiento sea una mierda. – Josh

+0

@Josh - No, el ejemplo está simplificado. La aplicación real está leyendo archivos de idioma para generar lexers y analizadores gramaticales. Estoy un poco oxidado en mi regex; mi problema parece tan obvio ahora! – Stomp

+0

@Josh: los motores Regex pueden hacer muchas optimizaciones para tales casos, incluyendo descartar muchos controles después de no poder hacer coincidir un prefijo común. Por ejemplo, si el primer carácter no es "i", ninguna de las ramas que comiencen con "i" se verificará. No estoy seguro si el motor de .NET hace esto, pero me sorprendería si no lo hiciera. –

Respuesta

12

La pereza y la codicia se aplica solo a los cuantificadores (?, *, +, {min,max}). Las alternancias siempre coinciden en orden y prueba la primera coincidencia posible.

+0

+1, debe haber cuantificadores para la avaricia por venir. – codaddict

+0

¿No tiene otra opción que reordenar? Hrmmm ... creo que podría volver a pedirlo sobre la marcha para que pueda mantener la definición en orden alfabético ... – Stomp

+0

@Stomp: Sí, eso se puede hacer. Mantenga la lista alfabética en el programa y justo antes de que la aplique, puede ordenarla por longitud. – codaddict

3

De acuerdo con RegularExpressions.info, las expresiones regulares son eager. Por lo tanto, cuando pasa por su piped expression, se detiene en la primera coincidencia sólida.

Mi recomendación sería almacenar todas sus palabras clave en una matriz o lista, y luego generar la expresión canalizada ordenada cuando la necesite. Solo tendrías que hacer esto una vez siempre y cuando tu lista de palabras clave no cambie. Simplemente almacene la expresión generada en un singleton de algún tipo y regrésela en las ejecuciones de expresiones regulares.

+0

@Jeras - ¡Gracias por los enlaces! Estaba buscando en MSDN y debo haber perdido que estaba buscando ansiosamente el primer partido. – Stomp

6

Parece que estás tratando de romper las cosas. Para hacer eso necesitas que toda la expresión sea correcta, la actual no lo es. Prueba este lugar ..

new Regex(@"\b(in|int|into|internal|interface)\b"); 

El "\ b" dice para que coincida con los límites de palabra, y es un partido de anchura cero. Este es el comportamiento dependiente de la configuración regional, pero en general esto significa espacios en blanco y puntuación. Al ser una coincidencia de ancho cero, no contendrá el carácter que causó que el motor de expresiones regulares detecte el límite de palabras.

+1

Agregar '\ b' provocará el comportamiento deseado, pero se equivoca acerca de cómo funciona. '\ b' es una aserción de ancho cero como'^',' $ 'y lookarounds; en lugar de emparejar un personaje, coincide con la brecha imaginaria * antes o después * de un personaje. El principio o el final de una cadena es automáticamente un límite de palabra si el primer o el último carácter (respectivamente) es un carácter de palabra, por lo que su segunda expresión regular es simplemente una versión más detallada de la primera. –

+0

@ Alan, intenté ejecutar el código, y está claro que tienes razón. Tendré que verificar el código en el trabajo para ver qué hacemos allí ... Tal vez estamos usando \ W y no \ b. Sé que estábamos obteniendo caracteres "sin palabras" de algún tipo en una situación similar en la que sé que teníamos algunos funky grupos de captura de mediodía configuración. En cuanto a que sea sensible a la configuración regional, ese será el caso, ya que los límites de las palabras se definirán de forma diferente según el rol de la puntuación. –

+0

@ Alan, modifiqué mi respuesta para reflejar sus comentarios. –

Cuestiones relacionadas