48 votes

Supprimer les commentaires C et C++ en utilisant Python ?

Je cherche du code Python qui supprime les commentaires C et C++ d'une chaîne. (Supposez que la chaîne contient un fichier source C complet.)

Je suis conscient que je pourrais .match() des sous-chaînes avec une Regex, mais cela ne résout pas l'imbrication /*, ou avoir un // à l'intérieur d'un /* */.

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

0 votes

Pourquoi diable voudriez-vous supprimer des commentaires du code source ???

2 votes

@QuantumPete, pour améliorer la lisibilité et la compréhension. L'approche la plus rapide est d'utiliser un éditeur de couleur et de définir la couleur des commentaires égale à la couleur de fond.

2 votes

@QuantumPete Ou parce que nous essayons de prétraiter le code source pour un processeur ultérieur qui ne prend pas en charge les commentaires sains

98voto

Markus Jarderot Points 33893

Ceci gère les commentaires de type C++, les commentaires de style C, les chaînes de caractères et les imbriquations simples de ceux-ci.

def comment_remover(text):
    def replacer(match):
        s = match.group(0)
        if s.startswith('/'):
            return " " # note : un espace et non une chaîne vide
        else:
            return s
    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )
    return re.sub(pattern, replacer, text)

Les chaînes de caractères doivent être incluses, car les marqueurs de commentaires à l'intérieur d'elles ne commencent pas un commentaire.

Éditer : re.sub ne prenait aucun drapeau, donc il a fallu compiler le motif d'abord.

Éditer 2 : Ajouté des littéraux de caractères, car ils pourraient contenir des guillemets qui seraient autrement reconnus comme des délimiteurs de chaîne de caractères.

Éditer 3 : Corrigé le cas où une expression légale int/**/x=5; deviendrait intx=5; qui ne compilerait pas, en remplaçant le commentaire par un espace plutôt qu'une chaîne vide.

0 votes

Cela ne gère pas les caractères « échappés » dans les chaînes de caractères. eg: char some_punctuation_chars=".\"/"; /* commentaire */

0 votes

Oui. \\. correspondra à n'importe quel caractère échappé, y compris \".

4 votes

De plus, vous pouvez conserver la numérotation des lignes par rapport au fichier d'entrée en modifiant le premier retour en: return "" + "\n" * s.count('\n') J'ai dû faire cela dans ma situation.

29voto

Konrad Rudolph Points 231505

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

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

Cela nécessite le drapeau "Single line" (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.

/MODIFIER : Remarquez que mon code ci-dessus fait en fait une hypothèse sur les fins de ligne ! Ce code ne fonctionnera pas sur un fichier texte Mac. Cependant, cela peut être corrigé relativement facilement :

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

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

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

3 votes

1. utilisez $ et re.MULTILINE au lieu de `'\n', '\r\n', etc

0 votes

Cela ne prend pas en charge le cas d'une ligne se terminant par un antislash, ce qui indique une ligne continue, mais ce cas est extrêmement rare.

0 votes

Vous avez oublié la chaîne vide de remplacement dans le re.sub. En outre, cela ne fonctionnera pas pour les chaînes. Par exemple, considérez 'string uncPath = "//some_path";' ou 'char operators[]="/*+-";' Pour l'analyse linguistique, je pense qu'il est préférable d'utiliser un vrai parseur.

7voto

Jonathan Leffler Points 299946

N'oubliez pas qu'en C, le retour à la ligne précédé d'un antislash est éliminé avant le traitement des commentaires, et les trigraphes sont traités avant cela (parce que ??/ est le trigraphe pour l'antislash). J'ai un programme C appelé SCC (supprimer les commentaires C/C++), et voici une partie du code de test...

" */ /* SCC a été formé pour connaître les chaînes de caractères /* */ */"!
"\"Guillemets doubles intégrés dans les chaînes de caractères, \\\" aussi\'!"
"Et \
des retours à la ligne dedans"

"Et des guillemets doubles échappés à la fin d'une chaîne de caractères\""

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

Ceci est suivi par le commentaire C++/C99 numéro 1.
// Commentaire C++/C99 avec \
caractère de continuation \
sur trois lignes de code source (cela ne devrait pas être vu avec l'option -C
Le commentaire C++/C99 numéro 1 est terminé.

Ceci est suivi par le commentaire C++/C99 numéro 2.
/\
/\
Commentaire C++/C99 (cela ne devrait pas être vu avec l'option -C)
Le commentaire C++/C99 numéro 2 est terminé.

Ceci est suivi par le commentaire C régulier numéro 1.
/\
*\
Commentaire
régulier
*\
/
Le commentaire C régulier numéro 1 est terminé.

/\
\/ Ceci n'est pas un commentaire C++/C99 !

Ceci est suivi par le commentaire C++/C99 numéro 3.
/\
\
\
/ Mais ceci est un commentaire C++/C99 !
Le commentaire C++/C99 numéro 3 est terminé.

/\
\* Ceci n'est pas un commentaire C ou C++ !

Ceci est suivi par le commentaire C régulier numéro 2.
/\
*/ Ceci est un commentaire C régulier *\
mais ceci est juste une continuation de routine *\
et ce n'était pas la fin non plus - mais ceci est *\
\
/
Le commentaire C régulier numéro 2 est terminé.

Ceci est suivi par le commentaire C régulier numéro 3.
/\
\
\
\
* Commentaire C */

Ceci n'illustre pas les trigraphes. Notez que vous pouvez avoir plusieurs antislash à la fin d'une ligne, mais le recollage des lignes n'a pas d'importance sur leur nombre, mais le traitement ultérieur pourrait en avoir. etc. Écrire une seule regex pour gérer tous ces cas sera complexe (mais cela est différent de l'impossible).

0 votes

Je rajouterais également que si quelqu'un écrivait un commentaire avec les symboles de début ou de fin de commentaire sur des lignes différentes, je les persuaderais de leur erreur. Et étendre un commentaire sur une seule ligne avec un backslash en fin de ligne est tout aussi maléfique. Donc, les problèmes ici sont plus imaginaires que réels - à moins que vous ne soyez un rédacteur de compilateur C.

6voto

Menno Rubingh Points 21

Cette publication propose une version codée de l'amélioration apportée au code de Markus Jarderot décrite par atikat dans un commentaire sur la publication de Markus Jarderot. (Merci à tous les deux d'avoir fourni le code original, qui m'a beaucoup aidé.)

Pour décrire l'amélioration un peu plus en détail : L'amélioration garde la numérotation des lignes intacte. (Cela est fait en préservant les caractères de retour à la 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 adaptée lorsque vous souhaitez générer des messages d'erreur pour vos utilisateurs (par exemple, erreurs d'analyse) contenant des numéros de ligne (c'est-à-dire des numéros de ligne valides pour le texte original).

import re

def removeCCppComment( texte ) :

    def blotOutNonNewlines( strIn ) :  # Retourne une chaîne contenant uniquement les caractères de retour à la ligne contenus dans strIn
        return "" + ("\n" * strIn.count('\n'))

    def remplaçant( correspondance ) :
        s = correspondance.group(0)
        if s.startswith('/'):  # La chaîne correspondante est //...FIN ou /*...*/  ==> Masquer tous les caractères qui ne sont pas des retours à la ligne
            return blotOutNonNewlines(s)
        else:                  # La chaîne correspondante est '...' ou "..."  ==> Garder inchangée
            return s

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

    return re.sub(modèle, remplaçant, texte)

5voto

zvoase Points 650

Je ne sais pas si vous êtes familier avec sed, le programme de traitement de texte basé sur UNIX (mais disponible sur Windows), mais j'ai trouvé un script sed ici qui supprimera les commentaires C/C++ d'un fichier. Il est très intelligent; par exemple, il ignorera '//' et '/*' s'ils sont trouvés dans une déclaration de chaîne, etc. Depuis Python, il peut être utilisé en utilisant le code suivant:

import subprocess
from cStringIO import StringIO

input = StringIO(source_code) # source_code est une chaîne avec le code source.
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, source_code est la variable contenant le code source C/C++, et finalement stripped_code contiendra le code C/C++ sans les commentaires. Bien sûr, si vous avez le fichier sur le disque, vous pourriez avoir les variables input et output être des gestionnaires de fichier pointant vers ces fichiers (input en mode lecture, output en mode écriture). remccoms3.sed est le fichier 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.

Cela sera probablement meilleur qu'une solution purement Python; pas besoin de réinventer la roue.

31 votes

Ne introduisez pas de dépendance supplémentaire en script et outil à votre script Python en utilisant Sed. Choisissez Sed ou Python, mais pas les deux.

2 votes

Ouvrir un autre processus n'est pas une bonne idée. C'est coûteux et risqué. Je suggère de rester avec du pur python.

2 votes

Ce n'est pas python. C'est shell. Comment faire sur Windows ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X