Fragmento de Python para eliminar comentarios de C y C++


Estoy buscando código Python que elimine los comentarios de C y C++ de una cadena. (Supongamos que la cadena contiene un archivo fuente C completo.)

Me doy cuenta de que podría .busca subcadenas con una expresión regular, pero eso no resuelve anidar /*, o tener un // dentro de un /* */.

Idealmente, preferiría una implementación no ingenua que maneje adecuadamente los casos incómodos.

Author: Greg Rogers, 2008-10-27

12 answers

No se si está familiarizado con sed, el programa de análisis de texto basado en UNIX (pero disponible en Windows), pero he encontrado un script sed aquí que eliminará los comentarios de C/C++ de un archivo. Es muy inteligente;por ejemplo, ignorará '/ / ' y '/ * ' si se encuentra en una declaración de cadena, etc. Desde dentro de Python, se puede usar usando el siguiente código:

import subprocess
from cStringIO import StringIO

input = StringIO(source_code) # source_code is a string with the source code.
output = StringIO()

process = subprocess.Popen(['sed', '/path/to/remccoms3.sed'],
    input=input, output=output)
return_code = process.wait()

stripped_code = output.getvalue()

En este programa, source_code es la variable que contiene el código fuente de C / C++, y finalmente stripped_code contendrá el código de C / C++ con los comentarios eliminados. Por supuesto, si tiene el archivo en el disco, podría tener las variables input y output como manejadores de archivos que apuntan a esos archivos (input en modo de lectura, output en modo de escritura). remccoms3.sed es el archivo del enlace anterior, y debe guardarse en una ubicación legible en el disco. sed también está disponible en Windows, y viene instalado por defecto en la mayoría de las distribuciones GNU/Linux y Mac OS X.

Esto probablemente será mejor que una solución pura de Python; no hay necesidad de reinventar el rueda.

 7
Author: zvoase,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-28 04:03:20

Esto maneja comentarios de estilo C++, comentarios de estilo C, cadenas y anidamiento simple de los mismos.

def comment_remover(text):
    def replacer(match):
        s = match.group(0)
        if s.startswith('/'):
            return " " # note: a space and not an empty string
        else:
            return s
    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )
    return re.sub(pattern, replacer, text)

Se deben incluir cadenas, porque los marcadores de comentarios dentro de ellas no inician un comentario.

Edit: re.sub no tomó ninguna bandera, así que tuvo que compilar el patrón primero.

Edit2: Se agregaron literales de caracteres, ya que podrían contener comillas que de otro modo serían reconocidas como delimitadores de cadena.

Edit3: Solucionado el caso en el que un la expresión int/**/x=5; se convertiría en intx=5; que no compilaría, reemplazando el comentario con un espacio en lugar de una cadena vacía.

 75
Author: Markus Jarderot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-19 17:45:27

Los comentarios de C (y C++) no se pueden anidar. Las expresiones regulares funcionan bien:

//.*?\n|/\*.*?\*/

Esto requiere la bandera de "Línea única" (Re.S) porque un comentario C puede abarcar varias líneas.

def stripcomments(text):
    return re.sub('//.*?\n|/\*.*?\*/', '', text, flags=re.S)

Este código debería funcionar.

/ EDIT: Observe que mi código anterior en realidad hace una suposición sobre los finales de línea! Este código no funcionará en un archivo de texto de Mac. Sin embargo, esto se puede modificar con relativa facilidad:

//.*?(\r\n?|\n)|/\*.*?\*/

Esta expresión regular debería funcionar en todos los archivos de texto, independientemente de sus finales de línea (cubre los finales de línea de Windows, Unix y Mac).

/EDIT: MizardX y Brian (en los comentarios) hicieron un comentario válido sobre el manejo de cadenas. Me olvidé completamente de eso porque la expresión regular anterior se extrae de un módulo de análisis que tiene un manejo adicional para cadenas. La solución de MizardX debería funcionar muy bien, pero solo maneja cadenas entre comillas dobles.

 25
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-09 06:38:35

No olvide que en C, la barra invertida-nueva línea se elimina antes de que se procesen los comentarios, y los trigraphs se procesan antes de eso (porque ??/ es el trigraph para barra invertida). Tengo un programa en C llamado SCC (strip C/C++ comments), y aquí es parte del código de prueba...

" */ /* SCC has been trained to know about strings /* */ */"!
"\"Double quotes embedded in strings, \\\" too\'!"
"And \
newlines in them"

"And escaped double quotes at the end of a string\""

aa '\\
n' OK
aa "\""
aa "\
\n"

This is followed by C++/C99 comment number 1.
// C++/C99 comment with \
continuation character \
on three source lines (this should not be seen with the -C fla
The C++/C99 comment number 1 has finished.

This is followed by C++/C99 comment number 2.
/\
/\
C++/C99 comment (this should not be seen with the -C flag)
The C++/C99 comment number 2 has finished.

This is followed by regular C comment number 1.
/\
*\
Regular
comment
*\
/
The regular C comment number 1 has finished.

/\
\/ This is not a C++/C99 comment!

This is followed by C++/C99 comment number 3.
/\
\
\
/ But this is a C++/C99 comment!
The C++/C99 comment number 3 has finished.

/\
\* This is not a C or C++  comment!

This is followed by regular C comment number 2.
/\
*/ This is a regular C comment *\
but this is just a routine continuation *\
and that was not the end either - but this is *\
\
/
The regular C comment number 2 has finished.

This is followed by regular C comment number 3.
/\
\
\
\
* C comment */

Esto no ilustra los trigramas. Tenga en cuenta que puede tener varias barras invertidas al final de una línea, pero el empalme de línea no se preocupa por cuántos hay, pero el procesamiento posterior sí. Sucesivamente. Escribir una sola expresión regular para manejar todos estos casos no será trivial (pero eso es diferente de imposible).

 6
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-28 02:57:06

Esta publicación proporciona una versión codificada de la mejora del código de Markus Jarderot que fue descrita por atikat, en un comentario a la publicación de Markus Jarderot. (Gracias a ambos por proporcionar el código original, que me ahorró mucho trabajo.)

Para describir la mejora algo más completamente: La mejora mantiene la numeración de líneas intacta. (Esto se hace manteniendo los caracteres de nueva línea intactos en las cadenas por las que se reemplazan los comentarios de C/C++.)

Esta versión de la función de eliminación de comentarios de C / C++ es adecuada cuando desea generar mensajes de error a sus usuarios (por ejemplo, errores de análisis) que contienen números de línea (es decir, números de línea válidos para el texto original).

import re

def removeCCppComment( text ) :

    def blotOutNonNewlines( strIn ) :  # Return a string containing only the newline chars contained in strIn
        return "" + ("\n" * strIn.count('\n'))

    def replacer( match ) :
        s = match.group(0)
        if s.startswith('/'):  # Matched string is //...EOL or /*...*/  ==> Blot out all non-newline chars
            return blotOutNonNewlines(s)
        else:                  # Matched string is '...' or "..."  ==> Keep unchanged
            return s

    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )

    return re.sub(pattern, replacer, text)
 6
Author: Menno Rubingh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-14 18:29:59

Los casos de expresión regular caerán en algunas situaciones, como cuando un literal de cadena contiene una subsecuencia que coincide con la sintaxis de comentario. Realmente necesitas un árbol de análisis para lidiar con esto.

 4
Author: Alex Coventry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-28 02:58:24

Puede aprovechar py++ para analizar el código fuente de C++ con GCC.

Py++ no reinventa la rueda. Se utiliza el compilador GCC C++ para analizar C++ archivos fuente. Para ser más precisos, el la cadena de herramientas se ve así:

El código fuente se pasa a GCC-XML GCC-XML lo pasa al compilador GCC C++ GCC-XML genera una descripción XML de un programa C++ de GCC interno representatividad. Py++ utiliza pygccxml paquete para leer GCC-XML generado file. El en pocas palabras-usted puede ser claro, que todas tus declaraciones son leer correctamente.

O, tal vez no. en cualquier caso, esto no es un análisis trivial.

@ RE soluciones basadas - es poco probable que encuentre un RE que maneje todos los posibles casos 'incómodos' correctamente, a menos que restrinja la entrada (por ejemplo, sin macros). para una solución a prueba de balas, realmente no tienes más remedio que aprovechar la gramática real.

 3
Author: Dustin Getz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-27 23:42:40

Lamento que esta no sea una solución de Python, pero también podría usar una herramienta que entienda cómo eliminar comentarios, como su preprocesador C/C++. Así es como GNU CPP lo hace.

cpp -fpreprocessed foo.c
 1
Author: sigjuice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-03 09:13:59

También hay una respuesta que no es de python: use el programa stripcmt :

StripCmt es una sencilla utilidad escrita en C para eliminar comentarios de C, C++, y archivos fuente Java. En el gran tradición del procesamiento de texto Unix programas, puede funcionar como un Filtro FIFO (Primero en entrar - Primero en Salir) o aceptar argumentos en la línea de comandos.

 1
Author: hlovdal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-18 14:18:07

Lo siguiente funcionó para mí:

from subprocess import check_output

class Util:
  def strip_comments(self,source_code):
    process = check_output(['cpp', '-fpreprocessed', source_code],shell=False)
    return process 

if __name__ == "__main__":
  util = Util()
  print util.strip_comments("somefile.ext")

Esta es una combinación del subproceso y el preprocesador cpp. Para mi proyecto tengo una clase de utilidad llamada "Util" que guardo varias herramientas que uso/necesito.

 1
Author: Antonio Arredondo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-25 05:27:45

Realmente no necesita un árbol de análisis para hacer esto perfectamente, pero en efecto necesita el flujo de tokens equivalente a lo que produce el front end del compilador. Tal flujo de token debe necessarilyy cuidar de todas las rarezas tales como inicio de comentario continuo de línea, inicio de comentario en cadena, normalización de trigraph, etc. Si tiene el flujo de tokens, eliminar los comentarios es fácil. (Tengo una herramienta que produce exactamente tales flujos de tokens, como, adivina qué, el front-end de un analizador real eso produce un verdadero árbol de análisis:).

El hecho de que los tokens sean reconocidos individualmente por expresiones regulares sugiere que, en principio, puede escribir una expresión regular que elija los lexemas de comentario. La complejidad real del conjunto de expresiones regulares para el tokenizador (al menos la que escribimos) sugiere que no se puede hacer esto en la práctica; escribirlas individualmente era bastante difícil. Si no quieres hacerlo perfectamente, bueno, entonces, la mayoría de las soluciones de RE anteriores son muy bien.

Ahora, why you would want strip comments is beyond me, unless you are building a code offuscator. En este caso, tienes que hacerlo perfectamente bien.

 0
Author: Ira Baxter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-03 08:38:46

Me encontré con este problema recientemente cuando tomé una clase en la que el profesor nos pidió que despojáramos a javadoc de nuestro código fuente antes de enviárselo para una revisión del código. Tuvimos que hacer esto varias veces, pero no pudimos simplemente eliminar el javadoc de forma permanente porque estábamos obligados a generar archivos html javadoc también. Aquí hay un pequeño script de Python que hice para hacer el truco. Dado que javadoc comienza con /** y termina con */, el script busca estos tokens, pero el script puede ser modificado para adaptarse a sus necesidades. También maneja los comentarios de bloque de una sola línea y los casos en los que un comentario de bloque termina, pero todavía hay código no comentado en la misma línea que el final del comentario de bloque. Espero que esto ayude!

ADVERTENCIA: Este script modifica el contenido de los archivos pasados y los guarda en los archivos originales. Sería prudente tener una copia de seguridad en otro lugar

#!/usr/bin/python
"""
 A simple script to remove block comments of the form /** */ from files
 Use example: ./strip_comments.py *.java
 Author: holdtotherod
 Created: 3/6/11
"""
import sys
import fileinput

for file in sys.argv[1:]:
    inBlockComment = False
    for line in fileinput.input(file, inplace = 1):
        if "/**" in line:
            inBlockComment = True
        if inBlockComment and "*/" in line:
            inBlockComment = False
            # If the */ isn't last, remove through the */
            if line.find("*/") != len(line) - 3:
                line = line[line.find("*/")+2:]
            else:
                continue
        if inBlockComment:
            continue
        sys.stdout.write(line)
 -1
Author: slottermoser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-07 16:10:54