Удалить комментарии из кода C/C++

Есть ли простой способ удалить комментарии из исходного файла C/C++ без какой-либо предварительной обработки. (т.е. я думаю, что вы можете использовать gcc -E, но это расширит макросы.) Я просто хочу удалить исходный код с комментариями, больше ничего не нужно менять.

РЕДАКТИРОВАТЬ:

Предпочтение существующему инструменту. Я не хочу писать это с помощью регулярных выражений, я предвижу слишком много сюрпризов в коде.

12 ответов

Решение

Запустите следующую команду в вашем исходном файле:

gcc -fpreprocessed -dD -E test.c

Спасибо KennyTM за то, что нашли правильные флаги. Вот результат для полноты:

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

Это зависит от того, насколько извращенными являются ваши комментарии. У меня есть программа scc раздеть комментарии C и C++. У меня также есть тестовый файл для него, и я попробовал GCC (4.2.1 на MacOS X) с опциями в текущем выбранном ответе - и GCC, кажется, не справляется с некоторыми из ужасно убитых комментариев в прецедент.

NB: Это не проблема реальной жизни - люди не пишут такой ужасный код.

Рассмотрим (подмножество - 36 из 135 строк) тестового примера:

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

На моем Mac выход из GCC (gcc -fpreprocessed -dD -E subset.c) является:

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

Выходные данные из 'scc':

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.

Вывод из 'scc -C' (который распознает комментарии с двойной косой чертой):

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.

Исходный код для SCC теперь доступен на GitHub

Текущая версия SCC - 6.60 (от 2016-06-12), хотя версии Git были созданы 2017-01-18 (в часовом поясе США / Тихого океана). Код доступен на GitHub по адресу https://github.com/jleffler/scc-snapshots. Вы также можете найти снимки предыдущих выпусков (4.03, 4.04, 5.05) и двух предварительных выпусков (6.16, 6.50) - все они помечены release/x.yz,

Код до сих пор в основном разрабатывается под RCS. Я все еще работаю над тем, как я хочу использовать субмодули или аналогичный механизм для работы с общими библиотечными файлами, такими как stderr.c а также stderr.h (который также можно найти по https://github.com/jleffler/soq).

SCC версии 6.60 пытается понять конструкции C++11, C++14 и C++17, такие как двоичные константы, числовая пунктуация, необработанные строки и шестнадцатеричные числа с плавающей запятой. По умолчанию используется режим C11. (Обратите внимание, что значение -C флаг - упомянутый выше - переключился между версией 4.0x, описанной в основном тексте ответа, и версией 6.60, которая в настоящее время является последней версией.)

gcc -fpreprocessed -dD -E не работает для меня, но эта программа делает это:

#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;
}

Существует программа stripcmt, которая может сделать это:

StripCmt - это простая утилита, написанная на C для удаления комментариев из исходных файлов C, C++ и Java. В соответствии с великой традицией программ обработки текста Unix, он может функционировать как фильтр FIFO (First In - First Out) или принимать аргументы в командной строке.

(за hlovdal: вопрос о коде Python для этого)

Это Perl-скрипт для удаления // однострочных и /* многострочных */ comments

  #!/usr/bin/perl

  undef $/;
  $text = <>;

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

  print $text;

Это требует вашего исходного файла в качестве аргумента командной строки. Сохраните сценарий в файл, скажем, remove_comments.pl и вызовите его с помощью следующей команды: perl -w remove_comments.pl [ваш исходный файл]

Надеюсь, это будет полезно

У меня тоже была эта проблема. Я нашел этот инструмент ( Cpp-Decomment), который работал для меня. Однако он игнорирует, если строка комментария продолжается до следующей строки. Например:

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

В этом случае я не мог найти путь в программе, поэтому просто искал пропущенные строки и исправил их вручную. Я полагаю, что был бы вариант для этого или, возможно, вы могли бы изменить исходный файл программы, чтобы сделать это.

Поскольку вы используете C, вы можете захотеть использовать что-то "естественное" для C. Вы можете использовать препроцессор C, чтобы просто удалить комментарии. Приведенные ниже примеры работают с препроцессором C от GCC. Они должны работать так же или аналогичным образом с другими C-процессорами.

Для C используйте

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

Это также работает для удаления комментариев из JSON, например, так:

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

Если ваш препроцессор C не доступен напрямую, вы можете попробовать заменить cpp с cc -E, который вызывает компилятор C и говорит остановить его после стадии препроцессора. В случае, если ваш двоичный компилятор C не cc Вы можете заменить cc с именем вашего бинарного компилятора C, например clang, Обратите внимание, что не все препроцессоры поддерживают -fpreprocessed,

Я пишу C-программу, используя стандартную C-библиотеку, около 200 строк, которая удаляет комментарии из файла с исходным кодом на C. https://github.com/qeatzy/removeccomments

поведение

  1. Комментарий в стиле C, занимающий несколько строк или занимающий всю строку, обнуляется.
  2. Комментарий в стиле C в середине строки остается без изменений. например, void init(/* do initialization */) {...}
  3. Комментарий в стиле C++, занимающий всю строку, обнуляется.
  4. Строковый литерал C соблюдается при проверке " а также \",
  5. обрабатывает продолжение строки. Если предыдущая строка заканчивается \текущая строка является частью предыдущей строки.
  6. номер строки остается прежним. Вычеркнутые строки или часть строки становятся пустыми.

тестирование и профилирование

Я тестировал самый большой исходный код cpython, который содержит много комментариев. В этом случае он делает работу правильно и быстро, в 2-5 раз быстрее, чем gcc

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

использование

/path/to/removeccomments < input_file > output_file

Недавно я написал код на Ruby для решения этой проблемы. Я рассмотрел следующие исключения:

  • комментарий в строках
  • многострочный комментарий на одной строке, исправление жадного совпадения.
  • несколько строк на несколько строк

Вот код: Github, Удалить комментарии

Он использует следующий код для предварительной обработки каждой строки в случае, если эти комментарии появляются в строках. Если это появляется в вашем коде, не повезло. Вы можете заменить его на более сложные строки.

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

Использование: ruby ​​-w inputfile outputfile

Я верю, если вы используете одно утверждение, вы можете легко удалить комментарии из C

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

Единственная проблема с этой командой, она не может удалить комментарии, содержащие более одной строки. Но с помощью этого regEx вы можете легко реализовать логику для многострочного удаления комментариев.

Я знаю, что уже поздно, но я решил поделиться своим кодом и первой попыткой написать компилятор.

Примечание: это не учитывает "\*/" внутри многострочного комментария, например /\*...."*/"...\*, Опять же, gcc 4.8.1 тоже нет.

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]);
     }
 }
#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;
}

Эта программа работает для обоих условий, т.е. // и /...../

Другие вопросы по тегам