Як я можу видалити всі символи, що підпадають під / * ... * /, включаючи / * & * /?


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 друку даних = sashelp.cars; бігати; дані abc; встановити ксиз; бігати;
Шарік Алам

1
Дякуємо за редагування Було б навіть краще, якщо ви також включили бажаний результат. Також включіть те, що ви спробували, і як це не вдалося, у питанні не в коментарях.
terdon

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. Розумний.
grochmal

14
Але cppзроблять набагато більше, ніж видаляти коментарі (обробляти #include, розширювати макроси, включаючи вбудовані ...)
Stéphane Chazelas

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% c% s% s% d \ n",
  1 - / * коментар * / - 1,
  / \
* коментар * /
  "/ * не коментар * /",
  / * багаторядковий
  коментар * /
  '"' / * коментар * /, '"',
  '\' ',' "'/ * коментар * /,
  '\
\
"', / * коментар * /
  "\\
"/ * не коментар * /",
  "?? /" / * не коментар * / ",
  '??' '+' "'/ *" коментар "* /);
  повернути 0;
}

Що дає цей вихід:

#include <stdio.h>
int main ()
{
  printf ("% d% s% c% 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...)
Stéphane Chazelas

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

6

з sed:

ОНОВЛЕННЯ

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

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

підтримка всього можливого (багаторядковий коментар, дані після [або і] befor,);

 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

@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'

Редагувати - скорочена версія Stephane:

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

добре, я погоджуюсь з тердоном: Давайте побачимо очікуваний результат.
Ганс Шоу

BTW: Що має статися з одним рядком, що містить: "/ * foo * / run; / * bar * /"? Потрібно це просто "запустити"; ?
Ганс Шоу

Чудово! Тоді моє рішення працює. Примітка: Я використовую не жадібний: ". +?"
Ганс Шоу

2
Дивіться -0777як коротший шляхBEGIN{$/=undef}
Стефан Шазелас

1
Можливо, .*?замість того, .+?якщо також /**/є вагомий коментар.
ilkkachu

2

Рішення за допомогою команди SED та без сценарію

Ось ви:

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

Примітка. Це не працює в ОС X, якщо ви не встановите gnu-sed. Але він працює на Linux Distros.


1
ви можете використовувати -iопцію для редагування файлу на місці замість перенаправлення виводу на новий файл. або набагато безпечніше -i.bakдля резервного копіювання
Рахул

1
Це також не працює для всіх випадків, спробуйте поставити коментар в одному рядку і дивіться, що відбувається ... Приклад встановити xy \; / * тест * / Я думаю, нам знадобиться 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 друкувати дані = sashelp.cars; бігати; дані abc; встановити ксиз; бігати;
Шарік Алам

@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.