Entfernen Sie Kommentare aus dem C / C ++ - Code

74

Gibt es eine einfache Möglichkeit, Kommentare aus einer C / C ++ - Quelldatei zu entfernen, ohne eine Vorverarbeitung durchzuführen? (dh ich denke, Sie können gcc -E verwenden, aber dies erweitert die Makros.) Ich möchte nur, dass der Quellcode mit Kommentaren entfernt wird, nichts anderes sollte geändert werden.

BEARBEITEN:

Bevorzugung eines vorhandenen Werkzeugs. Ich möchte dies nicht selbst mit regulären Ausdrücken schreiben müssen, ich sehe zu viele Überraschungen im Code voraus.

Mike
quelle
5
Dies ist eigentlich eine gute Übung für die Verwendung eines einfachen Lexers und Parsers!
Greg Hewgill
55
Dies ist eigentlich eine gute Übung für die Verwendung eines sehr komplizierten Lexers und Parsers.
4
@Pascal: Ich glaube Dr. Dobbs nicht, und gcc stimmt zu: error: pasting "/" and "/" does not give a valid preprocessing token- was erwartet wird, da das Entfernen von Kommentaren vor der Vorverarbeitung erfolgt
Christoph
2
@Neil: Entschuldigung, aber nein. Ein Parser befasst sich mit der Struktur von Anweisungen. Aus Sicht der Sprache ist ein Kommentar ein einzelnes Token, das an keiner größeren Struktur beteiligt ist. Es unterscheidet sich nicht von einem Leerzeichen (in der dritten Phase der Übersetzung muss jeder Kommentar durch ein einzelnes Leerzeichen ersetzt werden). Die Erklärung des Präprozessors in den Compiler ist viel einfacher: Der Präprozessor erzeugt häufig eine sehr große Ausgabe, sodass die Kommunikation mit dem Compiler die Kompilierungsgeschwindigkeit erheblich verbessert.
Jerry Coffin
7
@Neil: Vielleicht ist das das Beste - Sie scheinen nur die gleiche Behauptung zu wiederholen, ohne Belege. Sie haben noch nicht einmal darauf hingewiesen, welche semantische Analyse Ihrer Meinung nach erforderlich ist, um Kommentare korrekt zu analysieren. Sie haben nur wiederholt, dass dies der Fall ist (was der Standard nicht nur nicht erfordert, sondern auch nicht wirklich zulässt). Sie ersetzen Trigraphen, verbinden Linien und teilen die Quelle dann in Token und Sequenzen von Leerzeichen (einschließlich Kommentaren) auf. Wenn Sie versuchen, mehr Semantik als das zu berücksichtigen, machen Sie es falsch ...
Jerry Coffin

Antworten:

107

Führen Sie den folgenden Befehl für Ihre Quelldatei aus:

gcc -fpreprocessed -dD -E test.c

Vielen Dank an KennyTM für das Finden der richtigen Flaggen. Hier ist das Ergebnis der Vollständigkeit halber:

test.c:

#define foo bar
foo foo foo
#ifdef foo
#undef foo
#define foo baz
#endif
foo foo
/* comments? comments. */
// c++ style comments

gcc -fpreprocessed -dD -E test.c::

#define foo bar
foo foo foo
#ifdef foo
#undef foo
#define foo baz
#endif
foo foo
Josh Lee
quelle
3
Ich denke, das Ergebnis, das Mike erwartet, ist#define foo bar\nfoo foo foo
Pascal Cuoq
3
@Pascal: Laufen Sie gcc -fpreprocessed -dM -E test.c, um auch das #define-s zu erhalten , aber sie befinden sich nicht an den ursprünglichen Speicherorten.
Kennytm
14
Ich habe -P zu den gcc-Optionen hinzugefügt, um die seltsamen Linienmarkierungen zu unterdrücken, die manchmal angezeigt werden, wenn unsere Kommentare zum Start der Funktion entfernt werden.
Dana Robinson
2
Ich musste auch -P hinzufügen, um eine brauchbare Ausgabe zu erhalten.
James Johnston
1
Ich habe es einfach versucht und es und es hat die #included-Dateien eingefügt und die kommentierten Zeilen durch leere Zeilen ersetzt, anstatt die Kommentare zu löschen. FWIW eine Kombination aus sed und gcc hat bei mir immer perfekt funktioniert, siehe stackoverflow.com/a/13062682/1745001 .
Ed Morton
15

Es hängt davon ab, wie pervers Ihre Kommentare sind. Ich habe ein Programm scczum Entfernen von C- und C ++ - Kommentaren. Ich habe auch eine Testdatei dafür und habe GCC (4.2.1 unter MacOS X) mit den Optionen in der aktuell ausgewählten Antwort ausprobiert - und GCC scheint bei einigen der schrecklich geschlachteten Kommentare im Internet keine perfekte Arbeit zu leisten Testfall.

NB: Dies ist kein echtes Problem - die Leute schreiben keinen so schrecklichen Code.

Betrachten Sie die (Teilmenge - 36 von insgesamt 135 Zeilen) des Testfalls:

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

Auf meinem Mac gcc -fpreprocessed -dD -E subset.clautet die Ausgabe von GCC ( ):

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

Die Ausgabe von 'scc' ist:

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.

The regular C comment number 2 has finished.

This is followed by regular C comment number 3.

Die Ausgabe von 'scc -C' (die doppelte Schrägstriche erkennt) lautet:

The regular C comment number 1 has finished.

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

This is followed by C++/C99 comment number 3.

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.

The regular C comment number 2 has finished.

This is followed by regular C comment number 3.

Quelle für SCC jetzt auf GitHub verfügbar

Die aktuelle Version von SCC ist 6.60 (vom 12.06.2016), obwohl die Git-Versionen am 18.01.2017 (in der Zeitzone USA / Pazifik) erstellt wurden. Der Code ist bei GitHub unter https://github.com/jleffler/scc-snapshots erhältlich . Sie können auch Snapshots der vorherigen Versionen (4.03, 4.04, 5.05) und zwei Vorversionen (6.16, 6.50) finden - diese sind alle mit Tags versehen release/x.yz.

Der Code wird noch hauptsächlich unter RCS entwickelt. Ich arbeite immer noch daran, wie ich Submodule oder einen ähnlichen Mechanismus verwenden möchte, um allgemeine Bibliotheksdateien wie stderr.cund zu verarbeiten stderr.h(die auch unter https://github.com/jleffler/soq zu finden sind ).

SCC Version 6.60 versucht, C ++ 11-, C ++ 14- und C ++ 17-Konstrukte wie Binärkonstanten, numerische Interpunktion, rohe Zeichenfolgen und hexadezimale Gleitkommazahlen zu verstehen. Standardmäßig wird der C11-Modus verwendet. (Beachten Sie, dass die Bedeutung des -Coben erwähnten Flags zwischen der im Hauptteil der Antwort beschriebenen Version 4.0x und der Version 6.60, die derzeit die neueste Version ist, gewechselt ist.)

Jonathan Leffler
quelle
5
Glauben Sie mir, Jonathan, das tun sie. Ich habe den Code gelöscht und es gab 2000 Codezeilen, die kommentiert wurden. Ich konnte nicht glauben, wie ein Mensch diesen chaotischen Code schreiben kann.
Halil Kaskavalci
Könnten Sie dieses Programm veröffentlichen und den Link hier bitte geben? (wenn es libre / freie Software ist)
Totor
@ Motor: Es ist freie / libre (standardmäßig GPL v3) Software. Schicken Sie mir eine E-Mail und ich sende sie (meine E-Mail-Adresse befindet sich in meinem Profil). Ich habe einfach nirgendwo einen Ort, an dem ich routinemäßig solchen Code veröffentliche (erbärmlich, nicht wahr?).
Jonathan Leffler
@ JonathanLeffler Warum nicht deinen Code auf so etwas wie GitHub veröffentlichen?
Mads Hansen
@ JonathanLeffler kannst du es auf gists.github.com setzen? Ich brauche es.
noɥʇʎԀʎzɐɹƆ
8

gcc -fpreprocessed -dD -E hat bei mir nicht funktioniert, aber dieses Programm macht es:

#include <stdio.h>

static void process(FILE *f)
{
 int c;
 while ( (c=getc(f)) != EOF )
 {
  if (c=='\'' || c=='"')            /* literal */
  {
   int q=c;
   do
   {
    putchar(c);
    if (c=='\\') putchar(getc(f));
    c=getc(f);
   } while (c!=q);
   putchar(c);
  }
  else if (c=='/')              /* opening comment ? */
  {
   c=getc(f);
   if (c!='*')                  /* no, recover */
   {
    putchar('/');
    ungetc(c,f);
   }
   else
   {
    int p;
    putchar(' ');               /* replace comment with space */
    do
    {
     p=c;
     c=getc(f);
    } while (c!='/' || p!='*');
   }
  }
  else
  {
   putchar(c);
  }
 }
}

int main(int argc, char *argv[])
{
 process(stdin);
 return 0;
}
lhf
quelle
5
Behandelt keine Trigraphen.
OmnipotentEntity
7

Es gibt ein Stripcmt- Programm, das dies kann:

StripCmt ist ein einfaches Dienstprogramm, das in C geschrieben wurde, um Kommentare aus C-, C ++ - und Java-Quelldateien zu entfernen. In der großen Tradition von Unix-Textverarbeitungsprogrammen kann es entweder als FIFO-Filter (First In - First Out) fungieren oder Argumente in der Befehlszeile akzeptieren.

(per hlovdals Antwort auf: Frage zu Python-Code dafür )

che
quelle
1
Der Code hat noch einige Fehler. Zum Beispiel kann es nicht mit Code wie umgehen int /* comment // */ main().
Pynexj
und haben Fehler beim Umgang mit Kommentaren wie// comment out next line \
sleepsort
Meine Antwort behandelt diese Fälle. Es funktioniert perfekt, solange /*, //, */nicht in zwei Zeilen aufgeteilt.
Qeatzy
4

Dies ist ein Perl-Skript zum Entfernen von // einzeiligen und / * mehrzeiligen * / Kommentaren

  #!/usr/bin/perl

  undef $/;
  $text = <>;

  $text =~ s/\/\/[^\n\r]*(\n\r)?//g;
  $text =~ s/\/\*+([^*]|\*(?!\/))*\*+\///g;

  print $text;

Es erfordert Ihre Quelldatei als Befehlszeilenargument. Speichern Sie das Skript in einer Datei, sagen Sie remove_comments.pl, und rufen Sie es mit dem folgenden Befehl auf: perl -w remove_comments.pl [Ihre Quelldatei]

Hoffe es wird hilfreich sein

Vladimir
quelle
2
scheint nicht mit Saiten umzugehen , die "/*"oder "//"usw. im Kaninchenbau enthalten.
akavel
3

Ich hatte auch dieses Problem. Ich habe dieses Tool ( Cpp-Decomment ) gefunden, das für mich funktioniert hat. Es wird jedoch ignoriert, ob die Kommentarzeile bis zur nächsten Zeile reicht. Z.B:

// this is my comment \
comment continues ...

In diesem Fall konnte ich keinen Weg im Programm finden, also suchte ich einfach nach ignorierten Zeilen und korrigierte sie manuell. Ich glaube, dafür gibt es eine Option, oder Sie könnten die Quelldatei des Programms ändern, um dies zu tun.

Halil Kaskavalci
quelle
2

Da Sie C verwenden, möchten Sie möglicherweise etwas verwenden, das für C "natürlich" ist. Sie können den C-Präprozessor verwenden, um nur Kommentare zu entfernen. Die folgenden Beispiele funktionieren mit dem C-Präprozessor von GCC. Sie sollten auf die gleiche oder ähnliche Weise auch mit anderen C-Perprozessoren zusammenarbeiten.

Verwenden Sie für C

cpp -dD -fpreprocessed -o output.c input.c

Es funktioniert auch zum Entfernen von Kommentaren aus JSON, zum Beispiel wie folgt:

cpp -P -o - - <input.json >output.json

Falls Ihr C - Präprozessor nicht direkt zugänglich ist , können Sie versuchen , ersetzen cppmit cc -E, die den C - Compiler nennt es erzählt nach der Prä - Prozessor Bühne zu stoppen. Falls Ihre C-Compiler-Binärdatei nicht vorhanden ist cc, können Sie sie beispielsweise durch ccden Namen Ihrer C-Compiler-Binärdatei ersetzen clang. Beachten Sie, dass nicht alle Präprozessoren unterstützen -fpreprocessed.

Christian Hujer
quelle
1

Ich schreibe ein C-Programm mit einer Standard-C-Bibliothek, ungefähr 200 Zeilen, die Kommentare der C-Quellcodedatei entfernt. qeatzy / removeccomments

Verhalten

  1. Ein Kommentar im C-Stil, der sich über mehrere Zeilen erstreckt oder die gesamte Zeile belegt, wird auf Null gesetzt.
  2. Der Kommentar im C-Stil in der Mitte einer Zeile bleibt unverändert. z.B,void init(/* do initialization */) {...}
  3. Kommentare im C ++ - Stil, die die gesamte Zeile belegen, werden auf Null gesetzt.
  4. Das C-String-Literal wird durch Überprüfen von "und respektiert \".
  5. behandelt die Zeilenfortsetzung. Wenn die vorherige Zeile mit endet \, ist die aktuelle Zeile Teil der vorherigen Zeile.
  6. Zeilennummer bleibt gleich. Nullen oder Teile der Linie werden leer.

Testen & Profiling

Ich habe mit dem größten cpython-Quellcode getestet , der viele Kommentare enthält . In diesem Fall erledigt es die Arbeit korrekt und schnell, 2-5 schneller als gcc

time gcc -fpreprocessed -dD -E Modules/unicodeobject.c > res.c 2>/dev/null
time ./removeccomments < Modules/unicodeobject.c > result.c

Verwendung

/path/to/removeccomments < input_file > output_file
qeatzy
quelle
0

Ich glaube, wenn Sie eine Aussage verwenden, können Sie Kommentare einfach aus C entfernen

perl -i -pe ‘s/\\\*(.*)/g’ file.c This command Use for removing * C style comments 
perl -i -pe 's/\\\\(.*)/g' file.cpp This command Use for removing \ C++ Style Comments

Einziges Problem mit diesem Befehl: Kommentare, die mehr als eine Zeile enthalten, können nicht entfernt werden. Mit diesem regEx können Sie jedoch problemlos eine Logik zum Entfernen von Kommentaren mit mehreren Zeilen implementieren

Poseidon_Geek
quelle
0

Kürzlich habe ich Ruby-Code geschrieben, um dieses Problem zu lösen. Ich habe folgende Ausnahmen berücksichtigt:

  • Kommentar in Strings
  • mehrzeiliger Kommentar in einer Zeile, gierige Übereinstimmung korrigieren.
  • mehrere Zeilen in mehreren Zeilen

Hier ist der Code :

Es verwendet den folgenden Code, um jede Zeile vorzuverarbeiten, falls diese Kommentare in Zeichenfolgen angezeigt werden. Wenn es in Ihrem Code erscheint, ähm, Pech. Sie können es durch komplexere Zeichenfolgen ersetzen.

  • MUL_REPLACE_LEFT = " MUL_REPLACE_LEFT "
  • MUL_REPLACE_RIGHT = " MUL_REPLACE_RIGHT "
  • SIG_REPLACE = " SIG_REPLACE "

VERWENDUNG: ruby -w inputfile outputfile

chunyang.wen
quelle
-1

Ich weiß, dass es spät ist, aber ich dachte, ich würde meinen Code und meinen ersten Versuch, einen Compiler zu schreiben, teilen.

Hinweis: Dies gilt nicht für "\*/"einen mehrzeiligen Kommentar, z /\*...."*/"...\*. Andererseits tut gcc 4.8.1 dies auch nicht.

void function_removeComments(char *pchar_sourceFile, long long_sourceFileSize)
{
    long long_sourceFileIndex = 0;
    long long_logIndex = 0;

    int int_EOF = 0;

    for (long_sourceFileIndex=0; long_sourceFileIndex < long_sourceFileSize;long_sourceFileIndex++)
    {
        if (pchar_sourceFile[long_sourceFileIndex] == '/' && int_EOF == 0)
        {
            long_logIndex = long_sourceFileIndex;  // log "possible" start of comment

            if (long_sourceFileIndex+1 < long_sourceFileSize)  // array bounds check given we want to peek at the next character
            {
                if (pchar_sourceFile[long_sourceFileIndex+1] == '*') // multiline comment
                {
                    for (long_sourceFileIndex+=2;long_sourceFileIndex < long_sourceFileSize; long_sourceFileIndex++)
                    {
                        if (pchar_sourceFile[long_sourceFileIndex] == '*' && pchar_sourceFile[long_sourceFileIndex+1] == '/')
                        {
                            // since we've found the end of multiline comment
                            // we want to increment the pointer position two characters
                            // accounting for "*" and "/"
                            long_sourceFileIndex+=2;  

                            break;  // terminating sequence found
                        }
                    }

                    // didn't find terminating sequence so it must be eof.
                    // set file pointer position to initial comment start position
                    // so we can display file contents.
                    if (long_sourceFileIndex >= long_sourceFileSize)
                    {
                        long_sourceFileIndex = long_logIndex;

                        int_EOF = 1;
                    }
                }
                else if (pchar_sourceFile[long_sourceFileIndex+1] == '/')  // single line comment
                {
                    // since we know its a single line comment, increment file pointer
                    // until we encounter a new line or its the eof 
                    for (long_sourceFileIndex++; pchar_sourceFile[long_sourceFileIndex] != '\n' && pchar_sourceFile[long_sourceFileIndex] != '\0'; long_sourceFileIndex++);
                }
            }
        }

        printf("%c",pchar_sourceFile[long_sourceFileIndex]);
     }
 }
Johnny
quelle
Ich bin neugierig auf Ihren Kommentar "behandelt nicht". Ich kann nicht erkennen, was Sie denken, dass es nicht funktioniert. Beachten Sie, dass nach /*der Verarbeitung */der Kommentar durch die nächste Zeichenfolge ohne Leerzeichen beendet wird. In einem Kommentar gibt es keine Escape-Mechanismen - was Sie vielleicht damit meinen, dass GCC ihn auch nicht behandelt. Ihr Code hat Probleme mit "/* Magritte notes: Ceci n'est pas une commentaire */"(weil es sich um ein String-Literal handelt, nicht um einen Kommentar - aber er hat über Pipes gesprochen, nicht über Kommentare).
Jonathan Leffler
-3
#include<stdio.h>
{        
        char c;
        char tmp = '\0';
        int inside_comment = 0;  // A flag to check whether we are inside comment
        while((c = getchar()) != EOF) {
                if(tmp) {
                        if(c == '/') {
                                while((c = getchar()) !='\n');
                                tmp = '\0';
                                putchar('\n');
                                continue;
                        }else if(c == '*') {
                                inside_comment = 1;
                                while(inside_comment) {
                                        while((c = getchar()) != '*');
                                        c = getchar();
                                        if(c == '/'){
                                                tmp = '\0';
                                                inside_comment = 0;
                                        }
                                }
                                continue;
                        }else {
                                putchar(c);
                                tmp = '\0';
                                continue;
                        }
                }
                if(c == '/') {
                        tmp = c;
                } else {
                        putchar(c);
                }
        }
        return 0;
}

Dieses Programm läuft für beide Bedingungen, dh // und / ..... /

Vivek Patel
quelle
6
Mehrere Probleme. 1. Du vermisst int main(void). 2. Es werden keine Kommentarbegrenzer in Zeichenfolgenliteralen und Zeichenkonstanten behandelt. 3. Es löscht ein einzelnes /Zeichen (versuchen Sie es mit seinem eigenen Quellcode auszuführen).
Keith Thompson