Question

Je recherche un code Python qui supprime les commentaires C et C ++ d'une chaîne. (Supposons que la chaîne contienne un fichier source C complet.)

Je réalise que je pourrais utiliser des chaînes de caractères .match () avec un regex, mais cela ne résout pas l'imbrication / * , ni l'existence d'un // dans un / * * / .

Idéalement, je préférerais une implémentation non naïve qui gère correctement les cas difficiles.

Était-ce utile?

La solution

Je ne sais pas si vous connaissez bien sed , le programme d'analyse de texte basé sur UNIX (mais disponible sous Windows), mais j'ai trouvé un script sed ici qui supprimera les commentaires C / C ++ d'un fichier. C'est très intelligent; par exemple, il ignorera '//' et '/ *' s'il se trouve dans une déclaration de chaîne, etc. À partir de Python, il peut être utilisé avec le code suivant:

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()

Dans ce programme, code_source est la variable contenant le code source C / C ++ et, éventuellement, code-stripped contiendra le code C / C ++ avec les commentaires supprimés. Bien sûr, si vous avez le fichier sur le disque, les variables input et output peuvent être des descripteurs de fichier pointant sur ces fichiers ( input dans mode lecture, sortie en mode écriture). remccoms3.sed est le fichier à partir du lien ci-dessus et il devrait être enregistré dans un emplacement lisible sur le disque. sed est également disponible sur Windows et est installé par défaut sur la plupart des distributions GNU / Linux et Mac OS X.

Ce sera probablement mieux qu’une solution Python pure; pas besoin de réinventer la roue.

Autres conseils

Ceci gère les commentaires de style C ++, les commentaires de style C, les chaînes et leur imbrication simple.

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)

Les chaînes doivent être incluses car les marqueurs de commentaire qu'elles contiennent ne commencent pas à être commentées.

Modifier: re.sub n'a pris aucun indicateur, il a donc fallu d'abord compiler le modèle.

Edit2: les littéraux de caractères ajoutés, car ils pourraient contenir des guillemets qui seraient autrement reconnus comme des délimiteurs de chaîne.

Edit3: Le cas où une expression légale int / ** / x = 5; deviendrait intx = 5; , ce qui pas compiler, en remplaçant le commentaire par un espace plutôt que par une chaîne vide.

Les commentaires C (et C ++) ne peuvent pas être imbriqués. Les expressions régulières fonctionnent bien:

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

Ceci nécessite l'indicateur "Une seule ligne" ( Re.S ) car un commentaire C peut s'étendre sur plusieurs lignes.

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

Ce code devrait fonctionner.

/ EDIT: Notez que mon code ci-dessus fait une hypothèse sur les fins de ligne! Ce code ne fonctionnera pas sur un fichier texte Mac. Toutefois, cela peut être modifié assez facilement:

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

Cette expression régulière doit fonctionner sur tous les fichiers texte, quelle que soit leur fin de ligne (couvre les fins de ligne Windows, Unix et Mac).

/ EDIT: MizardX et Brian (dans les commentaires) ont formulé une remarque valable sur le traitement des chaînes. J'ai complètement oublié cela parce que la regex ci-dessus est extraite d'un module d'analyse qui dispose d'une gestion supplémentaire pour les chaînes. La solution de MizardX devrait très bien fonctionner, mais elle ne gère que les chaînes entre guillemets.

N'oubliez pas qu'en C, les barres obliques inverses sont supprimées avant le traitement des commentaires et que les trigrammes le sont avant (car ?? / est le trigramme de la barre oblique inversée). J'ai un programme C appelé SCC (strip C / C ++ comments), et voici une partie du code de test ...

" */ /* 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 */

Ceci n’illustre pas les trigrammes. Notez que vous pouvez avoir plusieurs barres obliques inverses à la fin d'une ligne, mais le fractionnement de lignes ne tient pas compte de leur nombre, mais du traitement ultérieur. Etc. Écrire une seule expression rationnelle pour traiter tous ces cas sera non trivial (mais c'est différent de l'impossible).

Cet article fournit une version codée de l'amélioration du code de Markus Jarderot décrite par atikat, dans un commentaire pour l'article de Markus Jarderot. (Merci aux deux pour avoir fourni le code original, ce qui m'a épargné beaucoup de travail.)

Pour décrire l’amélioration un peu plus en détail: L’amélioration maintient la numérotation des lignes intacte. (Pour ce faire, conservez les caractères de nouvelle ligne dans les chaînes par lesquelles les commentaires C / C ++ sont remplacés.)

Cette version de la fonction de suppression de commentaires C / C ++ est appropriée lorsque vous souhaitez générer des messages d'erreur à l'intention de vos utilisateurs (erreurs d'analyse, par exemple) contenant des numéros de ligne (c'est-à-dire des numéros de ligne valides pour le texte d'origine).

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)

Les cas d'expressions régulières vont tomber dans certaines situations, comme lorsqu'un littéral de chaîne contient une sous-séquence qui correspond à la syntaxe de commentaire. Vous avez vraiment besoin d'un arbre d'analyse pour résoudre ce problème.

vous pourrez peut-être utiliser py ++ pour analyser la source C ++ avec GCC.

  

Py ++ ne réinvente pas la roue. Il   utilise le compilateur GCC C ++ pour analyser le langage C ++   fichiers source. Pour être plus précis, le   la chaîne d'outils ressemble à ceci:

     

le code source est passé à GCC-XML   GCC-XML le transmet au compilateur GCC C ++   GCC-XML génère une description XML   d'un programme C ++ de l'interne de GCC   représentation. Py ++ utilise pygccxml   paquet pour lire GCC-XML généré   fichier. La ligne du bas - vous pouvez être   bien sûr, que toutes vos déclarations sont   lu correctement.

ou peut-être pas. peu importe, ce n’est pas une analyse triviale.

Solutions basées sur @ RE - il est peu probable que vous trouviez une ER qui gère correctement tous les cas «difficiles», à moins que vous ne contraigniez la saisie (par exemple, aucune macro). pour une solution à l'épreuve des balles, vous n'avez vraiment pas d'autre choix que de tirer parti de la vraie grammaire.

Je suis désolé, ce n’est pas une solution Python, mais vous pouvez également utiliser un outil permettant de supprimer les commentaires, comme votre préprocesseur C / C ++. Voici comment le GNP CPP le fait .

cpp -fpreprocessed foo.c

Il existe également une réponse non-Python: utilisez le programme stripcmt :

  

StripCmt est un utilitaire simple écrit   en C pour supprimer les commentaires de C, C ++,   et les fichiers source Java. Dans le grand   tradition du traitement de texte Unix   programmes, il peut fonctionner soit comme un   Filtre FIFO (premier entré - premier sorti) ou   accepte les arguments sur la ligne de commande.

Ce qui suit a fonctionné pour moi:

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")

Ceci est une combinaison du sous-processus et du préprocesseur cpp. Pour mon projet, j'ai une classe utilitaire appelée "Util". que je garde divers outils que j'utilise / dont j'ai besoin.

Vous n'avez pas vraiment besoin d'un arbre d'analyse pour le faire parfaitement, mais vous avez en fait besoin du flux de jetons équivalent à ce qui est produit par le front-end du compilateur. Un tel flux de jetons doit nécessairement prendre en charge toute l'étrangeté telle que le début du commentaire avec poursuite de ligne, le début du commentaire dans la chaîne, la normalisation du graphe, etc. Si vous avez le flux de jetons, la suppression des commentaires est simple. (J'ai un outil qui produit exactement ce type de flux de jetons, comme, devinez quoi, le frontal d'un véritable analyseur qui produit un véritable arbre d'analyse :).

Le fait que les jetons soient reconnus individuellement par des expressions rationnelles suggère que vous pouvez, en principe, écrire une expression rationnelle qui choisira les lexèmes de commentaire. La complexité réelle des expressions régulières définies pour le tokenizer (du moins celle que nous avons écrite) suggère que vous ne pouvez pas le faire en pratique; les écrire individuellement était déjà assez difficile. Si vous ne voulez pas le faire parfaitement, eh bien, alors la plupart des solutions d'énergies renouvelables ci-dessus sont très bien.

Maintenant, pourquoi vous voulez des commentaires de bande, ça me dépasse, sauf si vous construisez un code d'obscurcissement. Dans ce cas, vous devez avoir parfaitement raison.

J'ai rencontré ce problème récemment lorsque j'ai suivi un cours où le professeur nous a demandé de supprimer javadoc de notre code source avant de le lui soumettre pour une révision du code. Nous avons dû le faire plusieurs fois, mais nous ne pouvions pas simplement supprimer le javadoc de manière permanente, car nous devions également générer des fichiers HTML javadoc. Voici un petit script python que j'ai créé pour faire l'affaire. Javadoc commençant par / ** et se terminant par * /, le script recherche ces jetons, mais il peut être modifié pour répondre à vos besoins. Il gère également les commentaires de bloc sur une seule ligne et les cas dans lesquels un commentaire de bloc se termine mais où il reste du code non commenté sur la même ligne que le commentaire de bloc se terminant. J'espère que cela aide!

AVERTISSEMENT: ce script modifie le contenu des fichiers transmis et les enregistre dans les fichiers d'origine. Il serait sage d'avoir une sauvegarde ailleurs

#!/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)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top