web-dev-qa-db-fra.com

Utiliser regex pour supprimer les commentaires des fichiers source

Je crée un programme pour automatiser l'écriture de code C (j'écris pour analyser des chaînes en énumérations portant le même nom). La gestion des chaînes par C n'est pas terrible. Certaines personnes m'ont harcelé pour essayer python.

J'ai créé une fonction qui est supposée supprimer le C-style /* COMMENT */ et le //COMMENT D'une chaîne: Voici le code:

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

J'ai donc essayé ce code.

str="/* spam * spam */ eggs"
removeComments(str)
print str

Et cela n'a apparemment rien fait.

Des suggestions quant à ce que j'ai mal fait?

Il y a un dicton que j'ai entendu plusieurs fois:

Si vous avez un problème et que vous essayez de le résoudre avec Regex, vous vous retrouvez avec deux problèmes.


EDIT: En regardant cela des années plus tard. (après un peu plus d'expérience d'analyse)

Je pense que la regex était peut-être la bonne solution. Et la simple regex utilisée ici "assez bien". Je n'ai peut-être pas assez insisté sur ce point dans la question. C'était pour un seul fichier spécifique. Je pense qu’il serait beaucoup moins fastidieux de garder le fichier analysé assez simplement pour la regex, que de la compliquer, en une soupe de symboles illisible.

23
Lyndon White

re.sub renvoie une chaîne, donc changer votre code comme suit donnera des résultats:

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
28
msanders

Beaucoup de réponses sont déjà données mais
Qu'en est-il de "//comment-like strings inside quotes"?

OP demande comment faire en utilisant des expressions régulières; alors:

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)

CeciVAsupprime:

  • /* multi-line comments */
  • // single-line comments

NE SERA PAS enlever:

  • String var1 = "this is /* not a comment. */";
  • char *var2 = "this is // not a comment, either.";
  • url = 'http://not.comment.com';

Note : Ceci fonctionnera également pour Javascript source.

28
Onur Yıldırım

Je suggérerais d'utiliser un analyseur syntaxique réel comme SimpleParse ou PyParsing . SimpleParse nécessite que vous connaissiez réellement EBNF, mais est très rapide. PyParsing a sa propre syntaxe, semblable à celle de EBNF, mais elle est adaptée à Python et permet de créer des analyseurs syntaxiques extrêmement précis.

Modifier:  

Voici un exemple de la facilité d'utilisation de PyParsing dans ce contexte:

>>> test = '/* spam * spam */ eggs'
>>> import pyparsing
>>> comment = pyparsing.nestedExpr("/*", "*/").suppress()
>>> print comment.transformString(test)         
' eggs'

Voici un exemple plus complexe utilisant des commentaires sur une ou plusieurs lignes.

Avant:

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

Après:

>>> print comment.transformString(code)   

void
do_stuff ( int shoe, short foot ) {

     exciting_function(whee);
} 

Cela laisse une nouvelle ligne chaque fois qu'il supprime les commentaires, mais cela pourrait être résolu.

16
jathanism

Je vous recommanderais de lire cette page qui présente une analyse assez détaillée du problème et donne une bonne idée de la raison pour laquelle votre approche ne fonctionne pas: http://ostermiller.org/findcomment.html

Version courte: La regex que vous recherchez est la suivante:

(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)

Cela devrait correspondre aux deux types de blocs de commentaires. Si vous avez des difficultés à le suivre, lisez la page i liée.

4
MatsT

Tu le fais de la mauvaise manière. 

Regex est pour Langages réguliers , ce que C n'est pas. 

2
Otto Allmendinger

Je vois plusieurs choses que vous voudrez peut-être réviser.

Premièrement, Python transmet les objets par valeur, mais certains types d’objets sont immuables. Les chaînes et les entiers font partie de ces types immuables. Ainsi, si vous transmettez une chaîne à une fonction, les modifications apportées à la chaîne dans la fonction n'affecteront pas la chaîne que vous avez transmise. Vous devriez plutôt essayer de renvoyer une chaîne. De plus, dans la fonction removeComments (), vous devez affecter la valeur renvoyée par re.sub () à une nouvelle variable. Comme toute fonction prenant une chaîne en argument, re.sub () ne la modifiera pas.

Deuxièmement, je ferais écho à ce que d’autres ont dit à propos de l’analyse du code C. Les expressions régulières ne sont pas le meilleur moyen de s'y rendre.

1
jhoon
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("//")]

sortie

$ ./python.py

blah1


blah2
blah3
1
ghostdog74

Comme indiqué dans l'un de mes autres commentaires, l'imbrication de commentaires n'est pas vraiment le problème (en C, les commentaires ne sont pas imbriqués, bien que quelques compilateurs prennent en charge les commentaires imbriqués de toute façon). Le problème concerne des éléments tels que les littéraux de chaîne, qui peuvent contenir la même séquence de caractères qu'un délimiteur de commentaire sans en être réellement un.

Comme Mike Graham l'a dit, le bon outil pour ce travail est le lexer. Un analyseur est inutile et serait excessif, mais un lexer est exactement la bonne chose. En l'occurrence, j'ai posté un (partiel) lexer pour C (et C++) plus tôt ce matin. Il ne tente pas d’identifier correctement tous les éléments lexicaux (c’est-à-dire tous les mots-clés et les opérateurs) mais c’est tout à fait suffisant pour supprimer les commentaires. Cela n’apportera rien de bon en ce qui concerne l'utilisation de Python, car il est entièrement écrit en C (il est antérieur à mon utilisation de C++ pour bien plus que du code expérimental).

0
Jerry Coffin

Je veux juste ajouter une autre expression rationnelle dans laquelle nous devons supprimer tout ce qui est entre * et; en python

data = re.sub (re.compile ("*. *? \;", re.DOTALL), '', data)

il y a une barre oblique inverse avant * pour échapper au méta-caractère. 

0
Saurabh Sharma

Ce programme supprime les commentaires avec // et/* */du fichier donné:

#! /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')
0
harishli2020