Expresión regular para detectar C ++ finalizado en punto y coma para los bucles & while

En mi aplicación Python, necesito escribir una expresión regular que coincida con un bucle C ++ for o while que haya terminado con un punto y coma ( ; ). Por ejemplo, debería coincidir con esto:

 for (int i = 0; i < 10; i++); 

… pero no esto:

 for (int i = 0; i < 10; i++) 

Esto parece trivial a primera vista, hasta que te das cuenta de que el texto entre el paréntesis de apertura y de cierre puede contener otro paréntesis, por ejemplo:

 for (int i = funcA(); i < funcB(); i++); 

Estoy usando el módulo python.re. En este momento, mi expresión regular tiene este aspecto (he dejado mis comentarios para que puedas entenderlo más fácilmente):

 # match any line that begins with a "for" or "while" statement: ^\s*(for|while)\s* \( # match the initial opening parenthesis # Now make a named group 'balanced' which matches a balanced substring. (?P # A balanced substring is either something that is not a parenthesis: [^()] | # …or a parenthesised string: \( # A parenthesised string begins with an opening parenthesis (?P=balanced)* # …followed by a sequence of balanced substrings \) # …and ends with a closing parenthesis )* # Look for a sequence of balanced substrings \) # Finally, the outer closing parenthesis. # must end with a semi-colon to match: \s*;\s* 

Esto funciona perfectamente para todos los casos anteriores, pero se interrumpe tan pronto como usted intenta y hace que la tercera parte del bucle for contenga una función, como la siguiente:

 for (int i = 0; i < 10; doSomethingTo(i)); 

Creo que se rompe porque tan pronto como coloca un texto entre el paréntesis de apertura y el de cierre, el grupo “balanceado” coincide con el texto que contiene, y por lo tanto la parte (?P=balanced) ya no funciona, ya que no funcionará. coincidencia (debido al hecho de que el texto dentro del paréntesis es diferente).

En mi código de Python estoy usando los indicadores VERBOSE y MULTILINE, y estoy creando la expresión regular de esta manera:

 REGEX_STR = r"""# match any line that begins with a "for" or "while" statement: ^\s*(for|while)\s* \( # match the initial opening parenthesis # Now make a named group 'balanced' which matches # a balanced substring. (?P # A balanced substring is either something that is not a parenthesis: [^()] | # …or a parenthesised string: \( # A parenthesised string begins with an opening parenthesis (?P=balanced)* # …followed by a sequence of balanced substrings \) # …and ends with a closing parenthesis )* # Look for a sequence of balanced substrings \) # Finally, the outer closing parenthesis. # must end with a semi-colon to match: \s*;\s*""" REGEX_OBJ = re.compile(REGEX_STR, re.MULTILINE| re.VERBOSE) 

¿Alguien puede sugerir una mejora a esta expresión regular? Se está volviendo demasiado complicado para que me mueva la cabeza.

Podría escribir una rutina pequeña y muy simple que lo haga, sin usar una expresión regular:

  • Fije una posición pos contador de modo que apunte justo antes del soporte de apertura después de su for o while .
  • Establecer un contador de paréntesis abiertos openBr a 0 .
  • Ahora siga incrementando pos , leyendo los caracteres en las posiciones respectivas, e incremente openBr cuando vea un corchete de apertura, y openBr cuando vea un corchete de cierre. Eso lo incrementará una vez al principio, para el primer corchete de apertura en ” for ( “, incremente y decremente un poco más para algunos corchetes intermedios, y vuelva a ponerlo en 0 cuando se cierre el corchete.
  • Por lo tanto, deténgase cuando openBr ser 0 .

La posición de parada es su corchete de cierre for(...) . Ahora puedes comprobar si hay un punto y coma o no.

Este es el tipo de cosas que realmente no deberías hacer con una expresión regular. Simplemente analice la cadena un carácter a la vez, manteniendo un registro de los paréntesis de apertura / cierre.

Si esto es todo lo que está buscando, definitivamente no necesita un analizador / analizador de gramática C ++ en toda regla. Si quieres practicar, puedes escribir un pequeño analizador recursivo y decente, pero incluso eso es demasiado para unir paréntesis.

Este es un gran ejemplo del uso de la herramienta incorrecta para el trabajo. Las expresiones regulares no manejan sub-coincidencias anidadas arbitrariamente muy bien. Lo que debe hacer en su lugar es usar un lexer y un analizador reales (una gramática para C ++ debería ser fácil de encontrar) y buscar cuerpos de bucle inesperadamente vacíos.

Ni siquiera prestaría atención al contenido de los parens.

Simplemente haga coincidir cualquier línea que comience con for y termine con punto y coma:

 ^\t*for.+;$ 

A menos que tenga declaraciones divididas en varias líneas, ¿funcionará bien?

Prueba esta expresión regular

 ^\s*(for|while)\s* \( (?P [^()]* | (?P=balanced) \) \s*;\s 

Quité el envoltorio \( \) alrededor de (?P=balanced) y moví el * detrás de la secuencia any no paren. He tenido este trabajo con boost xpressive y volví a revisar ese sitio web ( Xpressive ) para actualizar mi memoria.

Greg es absolutamente correcto. Este tipo de análisis no se puede hacer con expresiones regulares. Supongo que es posible construir una monstruosidad horrenda que funcionaría en muchos casos, pero luego se encontrará con algo que sí funciona.

Realmente necesitas utilizar técnicas de análisis más tradicionales. Por ejemplo, es bastante simple escribir un analizador decente recursivo para hacer lo que necesita.

No sé que regex manejaría algo así muy bien. Prueba algo como esto

 line = line.Trim(); if(line.StartsWith("for") && line.EndsWith(";")){ //your code here } 

Otro pensamiento que ignora los paréntesis y trata el for como una construcción que contiene tres valores delimitados por punto y coma:

 for\s*\([^;]+;[^;]+;[^;]+\)\s*; 

Esta opción funciona incluso cuando se divide en varias líneas (una vez que MULTILINE está habilitado), pero asume que for ( ... ; ... ; ... ) es la única construcción válida, por lo que no funcionaría con un for ( x in y ) Construir, u otras desviaciones.

También supone que no hay funciones que contengan punto y coma como argumentos, como:

 for ( var i = 0; i < ListLen('a;b;c',';') ; i++ ); 

Si este es un caso probable, depende de para qué estás haciendo esto.

Como Frank sugirió, esto es mejor sin expresiones regulares. Aquí está (un feo) de una sola línea:

 match_string = orig_string[orig_string.index("("):len(orig_string)-orig_string[::-1].index(")")] 

Coincidiendo con la línea del troll se menciona en su comentario:

 orig_string = "for (int i = 0; i < 10; doSomethingTo(\"(\"));" match_string = orig_string[orig_string.index("("):len(orig_string)-orig_string[::-1].index(")")] 

devuelve (int i = 0; i < 10; doSomethingTo("("))

Esto funciona al correr a través de la cuerda hacia adelante hasta que alcanza el primer parén abierto, y luego hacia atrás hasta que alcanza el primer parén de cierre. A continuación, utiliza estos dos índices para cortar la cadena.