Jak mogę usunąć wszystkie znaki z / *… * / tym / * i * /?


12

Próbowałem sed i awk, ale to nie działa, ponieważ postać zawiera „/”, który jest już w dowodzie jako separator.

Daj mi znać, jak mogę to osiągnąć.

Poniżej znajduje się przykładowy przykład. Chcemy usunąć skomentowane sekcje, tj /*.....*/

/*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 Poniżej znajduje się wynik, który otrzymuję, pierwszy komentarz jest nadal dostępny. / * Służy do drukowania danych wyjściowych * / proc print data = sashelp.cars; biegać; dane abc; ustaw xyz; biegać;
Sharique Alam

1
Dzięki za edycję. Byłoby jeszcze lepiej, gdybyś uwzględnił również pożądaną wydajność. Uwzględnij również to, czego próbowałeś i jak to się nie powiodło w pytaniu, a nie w komentarzach.
terdon

2
Co powinno się stać z literałami łańcuchowymi zawierającymi komentarze lub ograniczniki komentarzy? (np. INSERT INTO string_table VALUES('/*'), ('*/'), ('/**/');)
zwolnij

1
Powiązane (przepraszam, że nie mogę się oprzeć!): Codegolf.stackexchange.com/questions/48326/…
ilkkachu

Zaktualizowałem mój post za pomocą innych rozwiązań, sprawdź ponownie, czy teraz jest dla Ciebie dobry.
Luciano Andress Martini

Odpowiedzi:


22

Myślę, że znalazłem łatwe rozwiązanie!

cpp -P yourcommentedfile.txt 

NIEKTÓRE AKTUALIZACJE:

Cytat od użytkownika ilkachu (oryginalny tekst z komentarzy użytkownika):

Grałem trochę z opcjami gcc: -fpreprocessor wyłączy większość dyrektyw i rozszerzeń makr (z wyjątkiem #define i #undef najwyraźniej). Dodanie -dD spowoduje także pozostawienie definicji; a std = c89 może być użyte do zignorowania nowego stylu // komentarzy. Nawet przy nich cpp zastępuje komentarze spacjami (zamiast je usuwać) i zwija spacje i puste linie.

Ale myślę, że nadal jest rozsądne i łatwe rozwiązanie w większości przypadków, jeśli wyłączysz rozwijanie makr i inne rzeczy, myślę, że uzyskasz dobre wyniki ... - i tak, możesz połączyć to ze skryptem powłoki, aby uzyskać lepsze ... i wiele więcej...


1
Korzystanie z preprocesora C jest prawdopodobnie najbardziej niezawodnym rozwiązaniem. Ponieważ preprocesor jest prawdopodobnie najsolidniejszym parserem komentarzy C. Sprytny.
grochmal

14
Ale cppzrobi o wiele więcej niż usuwanie komentarzy (przetwarzanie #include, rozwijanie makr, w tym wbudowanych ...)
Stéphane Chazelas

3
@LucianoAndressMartini, nie, tail -n +7po prostu usunie pierwsze 7 wierszy, nie zapobiegnie #includeprzetwarzaniu ani rozwinięciom makr. Spróbuj echo __LINE__ | cppna przykład. Lubecho '#include /dev/zero' | cpp
Stéphane Chazelas

2
Prawdopodobnie chcesz użyć -Ptrybu, jeśli to zrobisz. (Może to wyeliminować potrzebę używania tail.)
zwol

3
Grałem trochę z opcjami gcc: -fpreprocessedwyłączy większość dyrektyw i rozszerzeń makr (z wyjątkiem #definei #undefnajwyraźniej). Dodanie -dDpozostawi również definicje; i std=c89może służyć do ignorowania //komentarzy w nowym stylu . Nawet z nimi cppzastępuje komentarze spacjami (zamiast je usuwać) i zwija spacje i puste linie.
ilkkachu

10

Kiedyś wpadłem na pomysł, który możemy udoskonalić:

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'

aby obsłużyć jeszcze kilka skrzynek narożnych

Zauważ, że jeśli usuniesz komentarz, możesz zmienić znaczenie kodu ( 1-/* comment */-1parsowane jak 1 - -1podczas while 1--1(które uzyskasz , jeśli usuniesz komentarz) spowoduje błąd). Lepiej zastąpić komentarz spacją (tak jak tutaj), zamiast całkowicie go usunąć.

Powyższe powinno działać poprawnie na tym poprawnym kodzie ANSI C, na przykład, który próbuje uwzględnić kilka przypadków narożnych:

#include <stdio.h>
int main ()
{
  printf ("% d% s% c% c% c% c% c% s% s% d \ n",
  1 - / * komentarz * / - 1,
  / \
* komentarz */
  „/ * nie jest komentarzem * /”,
  / * multiline
  komentarz */
  '"' /* komentarz */ , '"',
  '\'','"'/* komentarz */,
  „\
\
"', /* komentarz */
  „\\
„/ * nie jest komentarzem * /”,
  „?? /” / * not a comment * / ”,
  „??” „+” „” / * „komentarz” * /);
  zwraca 0;
}

Co daje ten wynik:

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

  „/ * nie jest komentarzem * /”,

  „” „” „”
  „\”, „”
  „\
\
„”,  
  „\\
„/ * nie jest komentarzem * /”,
  „?? /” / * not a comment * / ”,
  „??” „+” „);
  zwraca 0;
}

Oba drukują ten sam wynik po kompilacji i uruchomieniu.

Możesz porównać z wyjściem, gcc -ansi -Eaby zobaczyć, co zrobiłby na nim procesor wstępny. Ten kod jest również prawidłowym kodem C99 lub C11, jednak gccdomyślnie wyłącza obsługę trigrafów, więc nie będzie działać, gccchyba że określisz standard, gcc -std=c99lub gcc -std=c11lub dodasz -trigraphsopcję).

Działa również z tym kodem C99 / C11 (nie ANSI / C90):

// komentarz
/ \
/ komentarz
// multiline \
komentarz
„// bez komentarza”

(porównaj z gcc -E/ gcc -std=c99 -E/ gcc -std=c11 -E)

ANSI C nie obsługiwał // formkomentarza. //nie jest inaczej ważne w ANSI C, więc nie pojawi się tam. Jednym z wymyślonych przypadków, w których //może rzeczywiście pojawić się w ANSI C (jak tam wspomniano , a resztę dyskusji może okazać się interesująca), jest użycie operatora strunizacji .

To jest poprawny kod ANSI C:

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

I w czasie dyskusji w 2004 r. gcc -ansi -ERzeczywiście ją rozszerzyłem "//not a comment". Jednak dzisiaj gcc-5.4zwraca błąd, więc wątpię, abyśmy znaleźli dużo kodu C przy użyciu tego rodzaju konstrukcji.

Odpowiednikiem GNU sedmoże być coś takiego:

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"

Jeśli GNU sedjest zbyt stary, by wspierać -Elub -zmożna wymienić pierwszą linię z:

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

Perl ma problem z wieloma liniami: przetestuj go z tym wyjściem => echo -e "BEGIN / * comment * / COMMAND / * com \ nment * / END"
بارپابابا

@Babby, działa dla mnie. Dodałem komentarz wielowierszowy i wynikowy wynik w moim przypadku testowym.
Stéphane Chazelas

Najlepszą rzeczą do porównania w dzisiejszych czasach jest gcc -std=c11 -E -P( -ansito po prostu inna nazwa -std=c90).
zwol

@zwol, chodzi o możliwość obsługi kodu napisanego dla dowolnego standardu C / C ++ (c90, c11 lub innego). Ściśle mówiąc, nie jest to możliwe (patrz mój drugi wymyślony przykład). Kod nadal próbuje obsługiwać konstrukcje C90 (jak ??'), dlatego porównujemy z cpp -ansitymi i C99 / C11 ... jeden (jak // xxx), dlatego porównujemy z cpp(lub cpp -std=c11...)
Stéphane Chazelas

@zwol, podzieliłem przypadek testowy, próbując trochę wyjaśnić. Wygląda na to, że trygrafy są nadal w C11, więc i tak mój drugi przypadek testowy nie jest standardowym C.
Stéphane Chazelas

6

z sed:

AKTUALIZACJA

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

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

obsługuje wszystkie możliwe (komentarz wieloliniowy, dane po [lub] przed,);

 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
-------------------
biegać:
$ sed -f command.sed FILENAME

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

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

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

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

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

nie będzie działać w przypadku komentarza rozpoczynającego się po danych, na przykładproc print data 2nd /*another comment is here*/
mazs

@mazs zaktualizowane, sprawdź
بارپابابا

To nie obsługuje komentarzy wewnątrz literałów łańcuchowych, co może mieć znaczenie, w zależności od tego, co robi SQL
zwolnij

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

 proc print data=sashelp.cars;
 run;

 data abc;
 set xyz;
 run;

Usuń puste linie, jeśli występują:

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

Edycja - krótsza wersja Stephane:

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

cóż, zgadzam się z terdon: pozwala zobaczyć oczekiwany wynik.
Hans Schou,

BTW: Co powinno się stać z pojedynczym wierszem zawierającym: „/ * foo * / run; / * bar * /”? Powinno to być po prostu „biegać”; ?
Hans Schou,

Świetny! Wtedy moje rozwiązanie działa. Uwaga Używam nie-chciwego: „. +?”
Hans Schou,

2
Zobacz -0777krótszy sposóbBEGIN{$/=undef}
Stéphane Chazelas

1
Być może .*?zamiast .+?jeśli /**/jest to również poprawny komentarz.
ilkkachu

2

Rozwiązanie za pomocą polecenia SED i bez skryptu

Tu masz:

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

Uwaga: To nie działa w systemie OS X, chyba że zainstalujesz gnu-sed. Ale działa na Linux Distros.


1
możesz użyć -iopcji edycji pliku w miejscu zamiast przekierowywania wyjścia do nowego pliku. lub znacznie bezpieczniejsze -i.baktworzenie kopii zapasowej pliku
Rahul

1
Nie działa również we wszystkich przypadkach, spróbuj umieścić komentarz w tym samym wierszu i obserwuj, co się stanie ... Przykład ustaw xy \; / * test * / Myślę, że będziemy potrzebować perla, aby rozwiązać to w łatwy sposób.
Luciano Andress Martini

@Rahul, dziękuję za wzmiankę. Chciałem tylko uprościć sprawę.
FarazX,

Przykro mi to mówić, że nie działa na komentarze w tej samej linii.
Luciano Andress Martini

@LucianoAndressMartini Teraz to robi!
FarazX,

1

seddziała na jednej linii na raz, ale niektóre komentarze na wejściu obejmują wiele linii. Zgodnie z /unix//a/152389/90751 , możesz najpierw użyć, traby zmienić łamanie linii w inną postać. Następnie sedmożesz przetworzyć dane wejściowe jako pojedynczą linię i użyć trponownie, aby przywrócić podział linii.

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

Użyłem bajtów zerowych, ale możesz wybrać dowolny znak, który nie pojawia się w pliku wejściowym.

*ma specjalne znaczenie w wyrażeniach regularnych, więc będzie musiał uciekać, \*aby dopasować literał *.

.*jest chciwy - będzie pasował do jak najdłuższego tekstu, w tym więcej */i /*. To oznacza pierwszy komentarz, ostatni komentarz i wszystko pomiędzy. Aby to ograniczyć, zamień .*na bardziej rygorystyczny wzorzec: komentarze mogą zawierać wszystko, co nie jest „*”, a także „*”, a po nim wszystko, co nie jest „/”. W przypadku wielu serii należy *również uwzględnić:

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

Spowoduje to usunięcie wszelkich podziałów wiersza w komentarzach wielowierszowych, tj.

data1 /* multiline
comment */ data2

stanie się

data1  data2

Jeśli nie tego chciał, sedmożna powiedzieć, że zachowa jeden z przełomów. Oznacza to wybranie znaku zastępującego podział linii, który można dopasować.

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

Nie \fmożna zagwarantować, że specjalny charakter i użycie odnośnika wstecznego, który może nic nie pasował, we wszystkich sedimplementacjach. (Potwierdziłem, że działa na GNU sed 4.07 i 4.2.2.)


Czy mógłbyś poinformować mne, jak to będzie działać. Próbowałem jak poniżej. tr '\ n' '\ 0' | sed -e 's, / * ([^ *] \ | * \ + [^ * /]) ** \ + / ,, g' test.sas | tr '\ 0' '\ n' i dostałem jak poniżej: / * To jest wydrukować dane wyjściowe * / data abcdf; ustaw cfgtr; biegać; proc print data = sashelp.cars; biegać; dane abc; ustaw xyz; biegać;
Sharique Alam,

@ShariqueAlam Umieściłeś tam test.sasw środku rurociągu, więc sedczyta z niego bezpośrednio, a pierwszy trnie ma wpływu. Musisz użyćcat test.sas | tr ...
JigglyNaga

0

za pomocą jednej linii sed do usunięcia komentarzy:

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

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.