Как я могу удалить все символы, попадающие в / *… * / включая / * & * /?


12

Я пробовал sed и awk, но он не работает, так как персонаж включает "/", который уже находится в команде в качестве разделителя.

Пожалуйста, дайте мне знать, как я могу этого достичь.

Ниже приведен пример примера. Мы хотим удалить закомментированные разделы, т.е. /*.....*/

/*This is to print the output
data*/
proc print data=sashelp.cars;
run;
/*Creating dataset*/
data abc;
set xyz;
run;

-bash-4.1 $ sed 's, / *. ** / ,, g' test.sas Ниже вывод, который я получаю, первый комментарий все еще там. / * Это для печати выходных данных * / proc print data = sashelp.cars; бегать; данные abc; установить xyz; бегать;
Шарик Алам

1
Спасибо за редактирование. Было бы еще лучше, если бы вы включили желаемый результат. Также включите то, что вы пытались и как это не удалось в вопросе, а не в комментариях.
Тердон

2
Что должно случиться со строковыми литералами, содержащими комментарии или разделители комментариев? (например INSERT INTO string_table VALUES('/*'), ('*/'), ('/**/');)
zwol

1
Связанные (извините, я не могу устоять!): Codegolf.stackexchange.com/questions/48326/…
ilkkachu

Я обновил свой пост другими решениями, пожалуйста, перепроверьте, если теперь это хорошо для вас.
Лучано Андресс Мартини

Ответы:


22

Я думаю, что нашел простое решение!

cpp -P yourcommentedfile.txt 

НЕКОТОРЫЕ ОБНОВЛЕНИЯ:

Цитата от пользователя ilkachu (оригинальный текст из комментариев пользователя):

Я немного поиграл с опциями для gcc: -fpreprocessed отключит большинство директив и расширений макросов (за исключением, очевидно, #define и #undef). Добавление -dD также оставит определения; и std = c89 можно использовать для игнорирования нового стиля // комментариев. Даже с ними, cpp заменяет комментарии пробелами (вместо того, чтобы удалять их) и сворачивает пробелы и пустые строки.

Но я думаю, что это по-прежнему разумно и легко для большинства случаев, если вы отключите расширение макроса и другие вещи, я думаю, вы получите хорошие результаты ... - и да, вы можете объединить это со сценарием оболочки для улучшения ... и многое другое...


1
Использование препроцессора C, вероятно, является наиболее надежным решением. Так как препроцессор, вероятно, самый надежный синтаксический анализатор C-комментариев. Умная.
Grochmal

14
Но cppмы сделаем гораздо больше, чем просто удаляем комментарии (обрабатываем #include, расширяем макросы, в том числе встроенные ...)
Стефан Шазелас

3
@LucianoAndressMartini, нет, tail -n +7просто удалит первые 7 строк, это не помешает #includeобработке или расширению макросов. Попробуйте, echo __LINE__ | cppнапример. Илиecho '#include /dev/zero' | cpp
Стефан Шазелас

2
Вы, вероятно, хотите использовать -Pрежим, если вы делаете это. (Это может исключить необходимость использования tail.)
zwol

3
Я немного поигрался с опциями для gcc: -fpreprocessedотключу большинство директив и расширений макросов (кроме #defineи #undefвидимо). Добавление -dDтакже оставляет определения в; и std=c89может использоваться, чтобы игнорировать //комментарии нового стиля . Даже с ними, cppзаменяет комментарии пробелами (вместо того, чтобы удалять их), и сворачивает пробелы и пустые строки.
ilkkachu

10

Однажды я придумал это, что мы можем улучшить:

perl -0777 -pe '
  BEGIN{
    $bs=qr{(?:\\|\?\?/)};
    $lc=qr{(?:$bs\n|$bs\r\n?)}
  }
  s{
    /$lc*\*.*?\*$lc*/
    | /$lc*/(?:$lc|[^\r\n])*
    | (
         "(?:$bs$lc*.|.)*?"
       | '\''$lc*(?:$bs$lc*(?:\?\?.|.))?(?:\?\?.|.)*?'\''
       | \?\?'\''
       | .[^'\''"/?]*
      )
  }{$1 eq "" ? " " : "$1"}exsg'

чтобы обработать еще несколько угловых случаев.

Обратите внимание, что если вы удалите комментарий, вы можете изменить значение кода ( 1-/* comment */-1анализируется, как, например, в 1 - -1то время как 1--1(который вы получите, если удалите комментарий), вы получите ошибку). Лучше заменить комментарий пробелом (как мы делаем здесь) вместо того, чтобы полностью удалить его.

Вышеприведенное должно корректно работать с этим действительным кодом ANSI C, например, который пытается включить несколько угловых случаев:

#include <stdio.h>
int main ()
{
  printf («% d% s% c% c% c% c% c% s% s% d \ n»,
  1 - / * комментарий * / - 1,
  / \
* комментарий * /
  "/ * не комментарий * /",
  / * многострочный
  комментарий * /
  '"' / * comment * /, '"',
  '\' ',' "'/ * комментарий * /,
  «\
\
"', / * комментарий * /
  «\\
"/ * не комментарий * /",
  "?? /" / * не комментарий * / ",
  '??' '+' "'/ *" comment "* /);
  вернуть 0;
}

Что дает этот вывод:

#include <stdio.h>
int main ()
{
  printf («% d% s% c% c% c% c% c% s% s% d \ n»,
  1- -1,

  "/ * не комментарий * /",

  '' ',' "',
  '\' ',' "',
  «\
\
«»,  
  «\\
"/ * не комментарий * /",
  "?? /" / * не комментарий * / ",
  '??' '+' "');
  вернуть 0;
}

Оба печатают один и тот же вывод при компиляции и запуске.

Вы можете сравнить с выводом, gcc -ansi -Eчтобы увидеть, что будет делать на нем препроцессор. Этот код также действует C99 или C11 код, однако gccотключает триграфы поддержки по умолчанию , поэтому он не будет работать с , gccесли не указано стандартом , как gcc -std=c99или gcc -std=c11или добавить -trigraphsопцию).

Он также работает с этим кодом C99 / C11 (не ANSI / C90):

// комментарий
/ \
/ комментарий
// многострочный \
комментарий
"// не комментарий"

(сравнить с gcc -E/ gcc -std=c99 -E/ gcc -std=c11 -E)

ANSI C не поддерживает // formкомментарий. //иначе не действует в ANSI C, поэтому не появится там. Один надуманный случай, когда //в ANSI C может действительно появиться (как отмечено там , а остальная часть обсуждения может оказаться интересной), это когда используется оператор stringify .

Это действительный код ANSI C:

#define s(x) #x
s(//not a comment)

И во время обсуждения в 2004 году gcc -ansi -Eдействительно расширилось до "//not a comment". Однако сегодня gcc-5.4возвращает ошибку, поэтому я сомневаюсь, что мы найдем много кода на C, использующего такую ​​конструкцию.

sedЭквивалентом GNU может быть что-то вроде:

lc='([\\%]\n|[\\%]\r\n?)'
sed -zE "
  s/_/_u/g;s/!/_b/g;s/</_l/g;s/>/_r/g;s/:/_c/g;s/;/_s/g;s/@/_a/g;s/%/_p/g;
  s@\?\?/@%@g;s@/$lc*\*@:&@g;s@\*$lc*/@;&@g
  s:/$lc*/:@&:g;s/\?\?'/!/g
  s#:/$lc*\*[^;]*;\*$lc*/|@/$lc*/$lc*|(\"([\\\\%]$lc*.|[^\\\\%\"])*\"|'$lc*([\\\\%]$lc*.)?[^\\\\%']*'|[^'\"@;:]+)#<\5>#g
  s/<>/ /g;s/!/??'/g;s@%@??/@g;s/[<>@:;]//g
  s/_p/%/g;s/_a/@/g;s/_s/;/g;s/_c/:/g;s/_r/>/g;s/_l/</g;s/_b/!/g;s/_u/_/g"

Если ваш GNU sedслишком стар для поддержки -Eили -z, вы можете заменить первую строку на:

sed -r ":1;\$!{N;b1}

У решения perl есть проблема с многострочным: протестируйте его с этим выводом => echo -e "BEGIN / * comment * / COMMAND / * com \ nment * / END"
بارپابابا

@ Babby, у меня работает. Я добавил многострочный комментарий и полученный результат в моем тестовом примере.
Стефан Шазелас

Лучшее, что можно сравнить с сегодняшним днем gcc -std=c11 -E -P( -ansiэто просто другое название -std=c90).
zwol

@zwol, идея состоит в том, чтобы иметь возможность обрабатывать код, написанный для любого стандарта C / C ++ (c90, c11 или другой). Строго говоря, это невозможно (см. Мой второй надуманный пример). Код все еще пытается обработать конструкции C90 (как ??'), поэтому мы сравниваем с ними cpp -ansiи C99 / C11 ... один (как // xxx), следовательно, мы сравниваем cpp(или cpp -std=c11...)
Стефан Шазелас

@zwol, я разделил тестовый пример, чтобы немного уточнить. Похоже, триграфы все еще в C11, так что мой второй контрольный пример не является стандартным C в любом случае.
Стефан Шазелас

6

с sed:

ОБНОВИТЬ

/\/\*/ {
    /\*\// {
        s/\/\*.*\*\///g;
        b next
    };

    :loop;
    /\*\//! {
        N;
        b loop
    };
    /\*\// {
        s/\/\*.*\*\//\n/g
    }
    :next
}

поддержка всех возможных (многострочный комментарий, данные после [или и] перед,);

 e1/*comment*/
-------------------
e1/*comment*/e2
-------------------
/*comment*/e2
-------------------
e1/*com
ment*/
-------------------
e1/*com
ment*/e2
-------------------
/*com
ment*/e2
-------------------
e1/*com
1
2
ment*/
-------------------
e1/*com
1
2
ment*/e2
-------------------
/*com
1
2
ment*/e2
-------------------
бегать:
$ sed -f command.sed FILENAME

e1
-------------------
e1e2
-------------------
e2
-------------------
e1

-------------------
e1
e2
-------------------

e2
-------------------
e1

-------------------
e1
e2
-------------------

e2
-------------------

не будет работать для комментариев, начинающихся после данных, какproc print data 2nd /*another comment is here*/
мази

@mazs обновлен, проверьте это
بارپابابا

Это не обрабатывает комментарии внутри строковых литералов, которые могут действительно иметь значение, в зависимости от того, что делает SQL
zwol

4
 $ cat file | perl -pe 'BEGIN{$/=undef}s!/\*.+?\*/!!sg'

 proc print data=sashelp.cars;
 run;

 data abc;
 set xyz;
 run;

Удалите пустые строки, если таковые имеются:

 $ cat file | perl -pe 'BEGIN{$/=undef}s!/\*.+?\*/\n?!!sg'

Edit - более короткая версия от Stephane:

 $ cat file | perl -0777 -pe 's!/\*.*?\*/!!sg'

хорошо, я согласен с Тердоном: давайте посмотрим на ожидаемый результат.
Ханс Шоу

Кстати: что должно произойти с одной строкой, содержащей: "/ * foo * / run; / * bar * /"? Должно ли это быть просто "бежать"? ?
Ганс Шоу

Большой! Тогда мое решение работает. Обратите внимание, я использую не жадные: ". +?"
Ганс Шоу

2
Смотрите -0777как более короткий способ сделатьBEGIN{$/=undef}
Стефан Шазелас

1
Возможно, .*?вместо .+?if /**/тоже правильный комментарий.
ilkkachu

2

Решение с помощью команды SED и без скрипта

А, вот и ты:

sed 's/\*\//\n&/g' test | sed '/\/\*/,/\*\//d'

NB Это не работает на OS X, если вы не установите gnu-sed. Но это работает на Linux Distros.


1
Вы можете использовать -iопцию для редактирования файла на месте вместо перенаправления вывода в новый файл. или намного безопаснее -i.bakдля файла резервной копии
Rahul

1
Это работает не во всех случаях, попробуйте поместить комментарий в одной строке и посмотреть, что произойдет ... Пример set xy \; / * test * / Я думаю, нам понадобится Perl, чтобы решить эту проблему легко.
Лучано Андресс Мартини

@Rahul точно, спасибо за упоминание. Я просто хотел сделать это проще.
FarazX

Мне очень жаль говорить, что это не работает для комментариев в той же строке.
Лучано Андресс Мартини

@LucianoAndressMartini Теперь это происходит!
FarazX

1

sedработает по одной строке за раз, но некоторые комментарии во входных данных охватывают несколько строк. Согласно /unix//a/152389/90751 , вы можете сначала использовать, trчтобы превратить разрывы строки в какой-то другой символ. Затем sedможете обработать ввод как одну строку, и вы trснова используете для восстановления разрывов строк.

tr '\n' '\0' | sed ... | tr '\0' \n'

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

*имеет особое значение в регулярных выражениях, поэтому его нужно экранировать, \*чтобы соответствовать литералу *.

.*является жадным - он будет соответствовать максимально длинному тексту, включая еще */и /*. Это означает первый комментарий, последний комментарий и все, что между ними. Чтобы ограничить это, замените .*его более строгим шаблоном: комментарии могут содержать все, что не является «*», а также «*», за которым следует все, что не является «/». Прогоны нескольких *s также должны быть учтены:

tr '\n' '\0' | sed -e 's,/\*\([^*]\|\*\+[^*/]\)*\*\+/,,g' | tr '\0' '\n'

Это удалит все разрывы строк в многострочных комментариях, т.е.

data1 /* multiline
comment */ data2

станет

data1  data2

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

tr '\n' '\f' | sed -e 's,/\*\(\(\f\)\|[^*]\|\*\+[^*/]\)*\*\+/,\2,g' | tr '\f' '\n'

Специальный символ \fи использование обратной ссылки, которая может ничего не соответствовать, не гарантированно будут работать должным образом во всех sedреализациях. (Я подтвердил, что работает на GNU sed 4.07 и 4.2.2.)


Не могли бы вы дать мне знать, как это будет работать. Я пытался, как показано ниже. tr '\ n' '\ 0' | sed -e 's, / * ([^ *] \ | * \ + [^ * /]) ** \ + / ,, g' test.sas | tr '\ 0' '\ n', и я получил, как показано ниже: / * Это для печати выходных данных * / data abcdf; установить cfgtr; бегать; proc print data = sashelp.cars; бегать; данные abc; установить xyz; бегать;
Шарик Алам

@ShariqueAlam Вы поместили test.sasтуда середину конвейера, поэтому sedсчитываете с него напрямую, и первое не trимеет никакого эффекта. Вы должны использоватьcat test.sas | tr ...
JigglyNaga

0

используя одну строку sed для удаления комментариев:

sed '/\/\*/d;/\*\//d' file

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.