Как я могу удалить все комментарии /* */ из исходного файла C?

У меня есть файл C, который я скопировал откуда-то еще, но в нем много комментариев, как показано ниже:

int matrix[20];
/* generate data */
for (index = 0 ;index < 20; index++)
matrix[index] = index + 1;
/* print original data */
for (index = 0; index < 5 ;index++)

Как я могу удалить все комментарии, заключенные в /* и */. Иногда комментарии состоят из 4-5 строк, и мне нужно удалить все эти строки.

По сути, мне нужно удалить весь текст между /* и */, и даже \n может оказаться между ними. Пожалуйста, помогите мне сделать это, используя один из sed, awk или perl.


person Vijay    schedule 11.11.2009    source источник
comment
Мне нравится это слово, но у меня есть файл C, но в нем много комментариев.   -  person innaM    schedule 11.11.2009
comment
@Manni ;-) см. " title="я неразумно отклоняю кандидатов с плохой орфографией и грамматикой"> stackoverflow.com/questions/1260273/   -  person Sinan Ünür    schedule 11.11.2009
comment
Без сомнения, некоторые комментарии сумасшедшие. Но избавиться от *всех комментариев?   -  person innaM    schedule 11.11.2009


Ответы (10)


Почему бы просто не использовать для этого препроцессор c? Почему вы ограничиваетесь доморощенным регулярным выражением?

[Изменить] Этот подход также корректно обрабатывает сценарий Бартса printf(".../*...").

Пример:

[File: t.c]
/* This is a comment */
int main () {
    /* 
     * This
     * is 
     * a
     * multiline
     * comment
     */
    int f = 42;
    /*
     * More comments
     */
    return 0;
}

.

$ cpp -P t.c
int main () {







    int f = 42;



    return 0;
}

Или вы можете удалить пробелы и сжать все

$ cpp -P t.c | egrep -v "^[ \t]*$"
int main () {
    int f = 42;
    return 0;
}

Нет смысла заново изобретать велосипед, не так ли?

[Изменить] Если вы хотите не расширять включенные файлы и макросы с помощью этого подхода, cpp предоставляет для этого флаги. Рассмотреть возможность:

[Файл: tc]

#include <stdio.h>
int main () {
    int f = 42;
    printf("   /*  ");
    printf("   */  ");
    return 0;
}

.

$ cpp -P -fpreprocessed t.c | grep -v "^[ \t]*$"
#include <stdio.h>
int main () {
    int f = 42;
    printf("   /*  ");
    printf("   */  ");
    return 0;
}

Есть небольшая оговорка: можно избежать расширения макроса, но исходное определение макроса удалено из источника.

person ezpz    schedule 11.11.2009
comment
Препроцессор имеет (потенциально нежелательный) побочный эффект: он также обрабатывает макросы, включает включаемые файлы и так далее... - person Raphaël Saint-Pierre; 11.11.2009
comment
Вы можете избавиться от расширения макроса с помощью -fpreprocessed. Я обновлю, чтобы упомянуть об этом - person ezpz; 11.11.2009
comment
-1 снова. Это не небольшое предостережение, если вы ожидаете, что исходный код скомпилируется после удаления комментариев. - person Sinan Ünür; 11.11.2009
comment
Эту оговорку можно исправить: perl -wpe 's/^\s*#define/#include#define/' your-file.c |cpp -P - -fpreprocessed|perl -wpe 's/#include#define/# include/ ---- это превращает любые #defines в (несколько недействительные) #includes, которые проходят через препроцессор, чтобы позже преобразовать обратно в правильные #defines. (Если вы согласны, добавьте это к самому ответу). - person Yaakov Belch; 12.11.2009
comment
Это прекрасно работает даже так: grep -v -E '^#' tutorial.thrift |cpp -P - person schemacs; 12.07.2014
comment
Разве ответ @ed-morton не является более полной версией этого? stackoverflow.com/a/13062670/355364 - person lpacheco; 24.11.2016

См. perlfaq6. Это довольно сложный сценарий.

$/ = undef;
$_ = <>;
s#/\*[^*]*\*+([^/*][^*]*\*+)*/|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#defined $2 ? $2 : ""#gse;
print;

Предупреждение: после того, как вы это сделаете, у вас есть тестовый сценарий, чтобы доказать себе, что вы только что удалили комментарии и ничего ценного? Если вы используете такое мощное регулярное выражение, я бы обеспечил какой-то тест (даже если вы просто записываете поведение до/после).

person Brian Agnew    schedule 11.11.2009
comment
Просто убедитесь, что двоичные файлы, созданные при компиляции, идентичны (отметки времени по модулю или другая идентификация сборки). - person ephemient; 11.11.2009
comment
Это может быть самое простое решение - person Brian Agnew; 11.11.2009
comment
Согласен, я бы никогда не сделал этого с кодом, который мне небезразличен, если бы у меня не было модульных тестов для проверки его правильности после его фильтрации. - person Ether; 11.11.2009

Взгляните на подпрограмму strip_comments в Inline::Filters:

sub strip_comments {
    my ($txt, $opn, $cls, @quotes) = @_;
    my $i = -1;
    while (++$i < length $txt) {
    my $closer;
        if (grep {my $r=substr($txt,$i,length($_)) eq $_; $closer=$_ if $r; $r}
        @quotes) {
        $i = skip_quoted($txt, $i, $closer);
        next;
        }
        if (substr($txt, $i, length($opn)) eq $opn) {
        my $e = index($txt, $cls, $i) + length($cls);
        substr($txt, $i, $e-$i) =~ s/[^\n]/ /g;
        $i--;
        next;
        }
    }
    return $txt;
}
person Sinan Ünür    schedule 11.11.2009

Пожалуйста, не используйте для этого cpp, если вы не понимаете последствий:

$ cat t.c
#include <stdio.h>

#define MSG "Hello World"

int main(void) {
    /* ANNOY: print MSG using the puts function */
    puts(MSG);
    return 0;
}

Теперь давайте прогоним его через cpp:

$ cpp -P t.c -fpreprocessed


#include <stdio.h>



int main(void) {


    puts(MSG);
    return 0;
}

Ясно, что этот файл больше не собирается компилироваться.

person Community    schedule 11.11.2009
comment
ну, во всяком случае, не после того, как вы добавите флаг -fpreprocessed - person Hasturkun; 11.11.2009
comment
@Hasturkun, и если вы не добавите -fpreprocessed, #include <stdio.h> будет расширен. - person Sinan Ünür; 11.11.2009
comment
Я пробовал это: perl -wpe 's/^\s*#define/#include#define/' your-file.c |cpp -P - -fpreprocessed|perl -wpe 's/#include#define/#include/ ---- это превращает любые #defines в (несколько недействительные) #includes, которые проходят через препроцессор, чтобы позже преобразовать обратно в правильные #defines. - person Yaakov Belch; 12.11.2009

Рассмотреть возможность:

printf("... /* ...");
int matrix[20];
printf("... */ ...");

Другими словами: я бы не стал использовать регулярное выражение для этой задачи, если только вы не выполняете replace-once и не уверены, что описанное выше не происходит.

person Bart Kiers    schedule 11.11.2009

Вы ДОЛЖНЫ использовать препроцессор C для этого в сочетании с другими инструментами, чтобы временно отключить определенные функции препроцессора, такие как расширение #define или #includes, все другие подходы не сработают в крайних случаях. Это будет работать для всех случаев:

[ $# -eq 2 ] && arg="$1" || arg=""
eval file="\$$#"
sed 's/a/aA/g;s/__/aB/g;s/#/aC/g' "$file" |
          gcc -P -E $arg - |
          sed 's/aC/#/g;s/aB/__/g;s/aA/a/g'

Поместите его в сценарий оболочки и вызовите его с именем файла, который вы хотите проанализировать, с необязательным префиксом, например, «-ansi», чтобы указать применяемый стандарт C.

person Ed Morton    schedule 25.10.2012
comment
Я подозреваю, что другие опубликованные решения выглядят проще, но, хотя это будет работать ВСЕ время, другие будут работать только НЕКОТОРЫЕ время, и любой, кто пытается это сделать, еще не столкнулся с тем случаем, когда их выбор решения терпит неудачу (или не заметил провал). Ах, похоже, я опубликовал это через 3 года после того, как был опубликован исходный вопрос, и ответ был принят, так что это, вероятно, главный фактор! - person Ed Morton; 15.04.2014
comment
Я мог вспомнить, что делал что-то подобное давным-давно. Вчера мне это снова понадобилось для чего-то быстрого, и я знал, что другие ответы не охватят все случаи. Хотел бы я снова проголосовать за него! - person Sam; 16.04.2014

Попробуйте это в командной строке (заменив «имена файлов» на список файлов, которые необходимо обработать):

perl -i -wpe 'BEGIN{undef $/} s!/\*.*?\*/!!sg' file-names

Эта программа изменяет файлы на месте (переписывая исходный файл с исправленным выводом). Если вы просто хотите получить вывод без изменения исходных файлов, опустите переключатель «-i».

Пояснение:

perl -- call the perl interpreter
-i      switch to 'change-in-place' mode.
-w      print warnings to STDOUT (if there are any)
 p      read the files and print $_ for each record; like while(<>){ ...; print $_;}
 e      process the following argument as a program (once for each input record)

BEGIN{undef $/} --- process whole files instead of individual lines.
s!      search and replace ...
  /\*     the starting /* marker
  .*?     followed by any text (not gredy search)
  \*/     followed by the */ marker
!!      replace by the empty string (i.e. remove comments)  
  s     treat newline characters \n like normal characters (remove multi-line comments)
   g    repeat as necessary to process all comments.

file-names   list of files to be processed.
person Yaakov Belch    schedule 11.11.2009
comment
@brian Принято: это лишь приблизительное решение. - person Yaakov Belch; 12.11.2009

Когда мне нужно что-то короткое и простое для CSS, я использую это:

awk -vRS='*/' '{gsub(/\/\*.*/,"")}1' FILE

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

Однако я считаю, что этот пуленепробиваемый.

person Craig Barnes    schedule 09.07.2012

Попробуйте приведенный ниже рекурсивный способ поиска и удаления комментариев типа сценария Java, комментариев типа XML и однострочных комментариев.

/* This is a multi line js comments.

Please remove me*/

для f в find pages/ -name "*.*"; do perl -i -wpe 'BEGIN{undef $/} s!/*.*?*/!!sg' $f; Готово

<!-- This is a multi line xml comments.

Please remove me -->

для f в find pages/ -name "*.*"; do perl -i -wpe 'BEGIN{undef $/} s!‹!--.*?-->!!sg' $f; Готово

//This is single line comment Please remove me.

для f в find pages/ -name "*.*"; do sed -i 's///.*//' $f; Готово

Примечание: страницы — это корневой каталог, и приведенный выше скрипт найдет и удалит все файлы, расположенные в корневом каталоге и подкаталогах.

person Joshua Paul    schedule 22.09.2014

очень упрощенный пример с использованием gawk. Пожалуйста, протестируйте много раз, прежде чем внедрять. Конечно, это не заботится о другом стиле комментариев // (в C++??)

$ more file
int matrix[20];
/* generate data */
for (index = 0 ;index < 20; index++)
matrix[index] = index + 1;
/* print original data */
for (index = 0; index < 5 ;index++)
/*
function(){
 blah blah
}
*/
float a;
float b;

$ awk -vRS='*/' '{ gsub(/\/\*.*/,"")}1' file
int matrix[20];


for (index = 0 ;index < 20; index++)
matrix[index] = index + 1;


for (index = 0; index < 5 ;index++)


float a;
float b;
person ghostdog74    schedule 11.11.2009
comment
по какой-то причине это не работает на моей машине :( cat test int matrix[20]; /* generate data */ for (index = 0 ;index < 20; index++) matrix[index] = index + 1; /* print original data */ и вывод awk -vRS='*/' '{ gsub(/\/\*.*/,"")}1' test int matrix[20]; / generate data / for (index = 0 ;index < 20; index++) matrix[index] = index + 1; / print original data / - person Vijay; 11.11.2009
comment
извините, комментарий настолько запутан, я не заметил, что у вас есть вывод. Ну, это сработало для меня. Я вижу, у вас все еще есть /сгенерировать данные/ и /распечатать исходные данные/. Как вы можете видеть из моего вывода, это работает для меня. - person ghostdog74; 11.11.2009
comment
если вы все еще не можете заставить его работать, ниже вы можете попробовать Perl-решение - person ghostdog74; 11.11.2009