python3 - Uso de expresiones regulares para eliminar comentarios de los archivos de origen
re python (10)
Como he señalado en uno de mis otros comentarios, el anidamiento de comentarios no es realmente el problema (en C, los comentarios no se anidan, aunque algunos compiladores admiten los comentarios anidados). El problema es con elementos como los literales de cadena, que pueden contener la misma secuencia de caracteres que un delimitador de comentarios sin ser realmente uno.
Como dijo Mike Graham, la herramienta adecuada para el trabajo es un lexer. Un analizador es innecesario y sería excesivo, pero un lexer es exactamente lo correcto. A medida que sucede, publiqué un lexer (parcial) para C (y C ++) esta mañana. No intenta identificar correctamente todos los elementos léxicos (es decir, todas las palabras clave y los operadores), pero es completamente suficiente para eliminar comentarios. Sin embargo, no servirá de nada en el frente de "uso de Python", ya que está escrito completamente en C (es anterior al uso de C ++ por mucho más que el código experimental).
Estoy creando un programa para automatizar la escritura de algunos códigos C, (escribo para analizar cadenas en enumeraciones con el mismo nombre) el manejo de las cadenas por parte de C no es tan bueno. Así que algunas personas me han estado molestando para que pruebe Python.
Hice una función que se supone que elimina C-style /* COMMENT */
y //COMMENT
de una cadena: Aquí está el código:
def removeComments(string):
re.sub(re.compile("//*.*?/*/",re.DOTALL ) ,"" ,string) # remove all occurance streamed comments (/*COMMENT */) from string
re.sub(re.compile("//.*?/n" ) ,"" ,string) # remove all occurance singleline comments (//COMMENT/n ) from string
Así que probé este código.
str="/* spam * spam */ eggs"
removeComments(str)
print str
Y al parecer no hizo nada.
¿Alguna sugerencia sobre lo que he hecho mal?
Hay un dicho que he escuchado un par de veces:
Si tienes un problema y tratas de resolverlo con Regex, terminas con dos problemas.
EDIT: Mirando hacia atrás en estos años más tarde. (Después de un poco más de experiencia de análisis)
Creo que regex puede haber sido la solución correcta. Y el simple regex utilizado aquí "lo suficientemente bueno". Puede que no haya enfatizado esto lo suficiente en la pregunta. Esto fue para un solo archivo específico. Eso no tuvo situaciones complicadas. Creo que sería mucho menos mantenimiento mantener el archivo analizado lo suficientemente simple para la expresión regular, que para complicar la expresión regular, en una sopa de símbolos ilegible.
Este programa elimina los comentarios con // y / * * / del archivo dado:
#! /usr/bin/python3
import sys
import re
if len(sys.argv)!=2:
exit("Syntax:python3 exe18.py inputfile.cc ")
else:
print (''The following files are given by you:'',sys.argv[0],sys.argv[1])
with open(sys.argv[1],''r'') as ifile:
newstring=re.sub(r''//*.*?/*/'','' '',ifile.read(),flags=re.S)
with open(sys.argv[1],''w'') as ifile:
ifile.write(newstring)
print(''/* */ have been removed from the inputfile'')
with open(sys.argv[1],''r'') as ifile:
newstring1=re.sub(r''//.*'','' '',ifile.read())
with open(sys.argv[1],''w'') as ifile:
ifile.write(newstring1)
print(''// have been removed from the inputfile'')
Le recomendaría que leyera esta página que tiene un análisis bastante detallado del problema y da una buena comprensión de por qué su enfoque no funciona: http://ostermiller.org/findcomment.html
Versión corta: la expresión regular que busca es la siguiente:
(//*([^*]|[/r/n]|(/*+([^*/]|[/r/n])))*/*+/)|(//.*)
Esto debería coincidir con ambos tipos de bloques de comentarios. Si tienes problemas para seguirlo, lee la página que he enlazado.
Lo estas haciendo mal.
Regex es para lenguajes regulares , que C no es.
Solo quiero agregar otra expresión regular donde tengamos que eliminar cualquier cosa entre * y; en pitón
data = re.sub (re.compile ("*. *? /;", re.DOTALL), '''', data)
hay una barra invertida antes de * para escapar del metacarácter.
Veo varias cosas que querrás revisar.
Primero, Python pasa los objetos por valor, pero algunos tipos de objetos son inmutables. Las cadenas y los enteros se encuentran entre estos tipos inmutables. Por lo tanto, si pasa una cadena a una función, cualquier cambio en la cadena que realice dentro de la función no afectará a la cadena que pasó. Debe intentar devolver una cadena en su lugar. Además, dentro de la función removeComments (), debe asignar el valor devuelto por re.sub () a una nueva variable, como cualquier función que tome una cadena como argumento, re.sub () no modificará la cadena.
En segundo lugar, me gustaría repetir lo que otros han dicho sobre el análisis del código C Las expresiones regulares no son la mejor manera de ir aquí.
Ya se dan muchas respuestas pero;
¿qué pasa con "//comment-like strings inside quotes"
?
OP está preguntando cómo hacerlo hacerlo usando expresiones regulares; asi que:
def remove_comments(string):
pattern = r"(/".*?/"|/'.*?/')|(//*.*?/*/|//[^/r/n]*$)"
# first group captures quoted strings (double or single)
# second group captures comments (//single-line or /* multi-line */)
regex = re.compile(pattern, re.MULTILINE|re.DOTALL)
def _replacer(match):
# if the 2nd group (capturing comments) is not None,
# it means we have captured a non-quoted (real) comment string.
if match.group(2) is not None:
return "" # so we will return empty to remove the comment
else: # otherwise, we will return the 1st group
return match.group(1) # captured quoted-string
return regex.sub(_replacer, string)
Esto eliminará:
-
/* multi-line comments */
-
// single-line comments
NO quitará:
-
String var1 = "this is /* not a comment. */";
-
char *var2 = "this is // not a comment, either.";
-
url = ''http://not.comment.com'';
Nota : Esto también funcionará para la fuente de Javascript .
Yo sugeriría usar un analizador REAL como SimpleParse o PyParsing . SimpleParse requiere que realmente sepas EBNF, pero es muy rápido. PyParsing tiene su propia sintaxis similar a EBNF, pero está adaptada para Python y hace que sea muy fácil crear analizadores potentes y precisos.
Editar:
Este es un ejemplo de lo fácil que es usar PyParsing en este contexto:
>>> test = ''/* spam * spam */ eggs''
>>> import pyparsing
>>> comment = pyparsing.nestedExpr("/*", "*/").suppress()
>>> print comment.transformString(test)
'' eggs''
Aquí hay un ejemplo más complejo utilizando comentarios de una sola línea o de varias líneas.
Antes de:
/*
* multiline comments
* abc 2323jklj
* this is the worst C code ever!!
*/
void
do_stuff ( int shoe, short foot ) {
/* this is a comment
* multiline again!
*/
exciting_function(whee);
} /* extraneous comment */
Después:
>>> print comment.transformString(code)
void
do_stuff ( int shoe, short foot ) {
exciting_function(whee);
}
Deja una nueva línea adicional donde sea que eliminó los comentarios, pero eso podría abordarse.
re.sub
devuelve una cadena, por lo que cambiar su código a lo siguiente dará resultados:
def removeComments(string):
string = re.sub(re.compile("//*.*?/*/",re.DOTALL ) ,"" ,string) # remove all occurance streamed comments (/*COMMENT */) from string
string = re.sub(re.compile("//.*?/n" ) ,"" ,string) # remove all occurance singleline comments (//COMMENT/n ) from string
return string
mystring="""
blah1 /* comments with
multiline */
blah2
blah3
// double slashes comments
blah4 // some junk comments
"""
for s in mystring.split("*/"):
s=s[:s.find("/*")]
print s[:s.find("//")]
salida
$ ./python.py
blah1
blah2
blah3