Удалить комментарии из кода 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) или принимать аргументы в командной строке.
Это 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
поведение
- Комментарий в стиле C, занимающий несколько строк или занимающий всю строку, обнуляется.
- Комментарий в стиле C в середине строки остается без изменений. например,
void init(/* do initialization */) {...}
- Комментарий в стиле C++, занимающий всю строку, обнуляется.
- Строковый литерал C соблюдается при проверке
"
а также\"
, - обрабатывает продолжение строки. Если предыдущая строка заканчивается
\
текущая строка является частью предыдущей строки. - номер строки остается прежним. Вычеркнутые строки или часть строки становятся пустыми.
тестирование и профилирование
Я тестировал самый большой исходный код 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;
}
Эта программа работает для обоих условий, т.е. // и /...../