Wie kann ich alle Zeichen löschen, die unter / *… * / fallen, einschließlich / * & * /?

12

Ich habe sed und awk ausprobiert, aber es funktioniert nicht, da es sich bei dem Zeichen um "/" handelt, das bereits als Trennzeichen im Befehl steht.

Bitte lassen Sie mich wissen, wie ich dies erreichen kann.

Unten sehen Sie ein Beispiel. Wir möchten die kommentierten Abschnitte entfernen, d. H /*.....*/

/*This is to print the output
data*/
proc print data=sashelp.cars;
run;
/*Creating dataset*/
data abc;
set xyz;
run;
Sharique Alam
quelle
-bash-4.1 $ sed 's, / *. ** / ,, g' test.sas Unten ist die Ausgabe, die ich bekomme, der erste Kommentar ist noch da. / * Hiermit werden die Ausgabedaten gedruckt * / proc print data = sashelp.cars; Lauf; Daten abc; setze xyz; Lauf;
Sharique Alam
1
Danke für die Bearbeitung. Es wäre sogar noch besser, wenn Sie auch Ihre gewünschte Ausgabe einbinden würden. Geben Sie in den Kommentaren auch an, was Sie versucht haben und wie es fehlgeschlagen ist.
Terdon
2
Was soll mit String-Literalen geschehen, die Kommentare oder Kommentarbegrenzer enthalten? (zB INSERT INTO string_table VALUES('/*'), ('*/'), ('/**/');)
zwol
1
Verwandte (sorry, ich kann nicht widerstehen!): Codegolf.stackexchange.com/questions/48326/…
ilkkachu
Ich habe meinen Beitrag mit einer anderen Lösung aktualisiert. Bitte überprüfen Sie erneut, ob dies jetzt für Sie gut ist.
Luciano Andress Martini

Antworten:

22

Ich denke, ich habe eine einfache Lösung gefunden!

cpp -P yourcommentedfile.txt 

EINIGE UPDATES:

Zitat des Benutzers ilkachu (Originaltext aus den Benutzerkommentaren):

Ich habe ein bisschen mit den Optionen für gcc gespielt: -fpreprocessed deaktiviert die meisten Direktiven und Makro-Erweiterungen (außer anscheinend #define und #undef). Das Hinzufügen von -dD hinterlässt ebenfalls defined in. und std = c89 können verwendet werden, um // neue Stilkommentare zu ignorieren. Selbst mit ihnen ersetzt cpp Kommentare durch Leerzeichen (anstatt sie zu entfernen) und reduziert Leerzeichen und leere Zeilen.

Aber ich denke, es ist immer noch vernünftig und eine einfache Lösung für die meisten Fälle, wenn Sie die Makroerweiterung deaktivieren und andere Dinge, von denen ich denke, dass Sie gute Ergebnisse erzielen ... - und ja, Sie können das mit Shell-Skript kombinieren, um besser zu werden ... und vieles mehr...

Luciano Andress Martini
quelle
1
Die Verwendung des C-Präprozessors ist wahrscheinlich die robusteste Lösung. Da der Präprozessor wahrscheinlich der robusteste Parser von C-Kommentaren ist. Klug.
Grochmal
14
Aber es cppwird viel mehr als das Entfernen von Kommentaren (Verarbeiten #include, Erweitern von Makros, einschließlich eingebauter Makros ...)
Stéphane Chazelas
3
@LucianoAndressMartini, nein, entfernt tail -n +7nur die ersten 7 Zeilen und verhindert nicht die #includeVerarbeitung oder Makroerweiterungen. Versuchen Sie es echo __LINE__ | cppzum Beispiel. Oderecho '#include /dev/zero' | cpp
Stéphane Chazelas
2
In diesem Fall möchten Sie wahrscheinlich den -PModus verwenden. (Dies kann die Notwendigkeit der Verwendung beseitigen tail.)
zwol
3
Ich habe ein bisschen mit den Optionen für gcc gespielt: -fpreprocessedDeaktiviert die meisten Direktiven und Makro-Erweiterungen (außer #defineund #undefanscheinend). Durch -dDdas Hinzufügen bleiben auch die Definitionen erhalten. und std=c89können verwendet werden, um neue //Stilkommentare zu ignorieren . Ersetzt auch bei ihnen cppKommentare durch Leerzeichen (anstatt sie zu entfernen) und reduziert Leerzeichen und leere Zeilen.
Ilkkachu
10

Ich kam einmal mit bis diese , die wir verfeinern:

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'

um ein paar weitere Eckfälle zu behandeln.

Beachten Sie, dass , wenn Sie entfernen einen Kommentar, können Sie die Bedeutung des Codes ändern könnten ( 1-/* comment */-1wird wie analysiert , 1 - -1während 1--1(die Sie erhalten würden , wenn Sie den Kommentar entfernt) würden Sie einen Fehler). Es ist besser, den Kommentar durch ein Leerzeichen zu ersetzen (wie wir es hier tun), als ihn vollständig zu entfernen.

Das Obige sollte zum Beispiel mit diesem gültigen ANSI-C-Code funktionieren, der versucht, einige Eckfälle einzuschließen:

#include <stdio.h>
int main ()
{
  printf ("% d% s% c% c% c% c% s% s% d \ n",
  1 - / * Kommentar * / - 1,
  / \
* Kommentar */
  "/ * kein Kommentar * /",
  / * mehrzeilig
  Kommentar */
  '"' /* Kommentar */ , '"',
  '\'','"'/* Kommentar */,
  '\
\
"', /* Kommentar */
  "\\
"/ * kein Kommentar * /",
  "?? /" / * kein Kommentar * / ",
  '??' '+' "'/ *" Kommentar "* /);
  return 0;
}

Was gibt diese Ausgabe:

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

  "/ * kein Kommentar * /",

  '"', '"',
  '\' ',' "',
  '\
\
"',  
  "\\
"/ * kein Kommentar * /",
  "?? /" / * kein Kommentar * / ",
  '??' '+' "');
  return 0;
}

Beide drucken beim Kompilieren und Ausführen dieselbe Ausgabe.

Sie können mit der Ausgabe von vergleichen, um gcc -ansi -Ezu sehen, was der Vorprozessor damit machen würde. Dieser Code ist auch gültig für C99- oder C11-Code, gccdeaktiviert jedoch standardmäßig die Trigraph-Unterstützung, sodass er nur funktioniert, gccwenn Sie den Standard wie gcc -std=c99oder angeben gcc -std=c11oder die -trigraphsOption hinzufügen .

Es funktioniert auch mit diesem C99 / C11-Code (kein ANSI / C90-Code):

// Kommentar
/ \
/ Kommentar
// mehrzeilig \
Kommentar
"// kein Kommentar"

(vergleiche mit gcc -E/ gcc -std=c99 -E/ gcc -std=c11 -E)

ANSI C hat den // formKommentar von nicht unterstützt . //ist in ANSI C sonst nicht gültig und wird dort nicht angezeigt. Ein erfundener Fall //, in dem ANSI C möglicherweise wirklich vorkommt (wie dort angegeben , und Sie den Rest der Diskussion vielleicht interessant finden), ist, wenn der Operator stringify verwendet wird.

Dies ist ein gültiger ANSI C-Code:

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

Und zum Zeitpunkt der Diskussion im Jahr 2004 hat gcc -ansi -Ees sich in der Tat erweitert "//not a comment". Gibt jedoch heute gcc-5.4einen Fehler zurück, sodass ich bezweifle, dass wir mit dieser Art von Konstrukt viel C-Code finden werden.

Das GNU- sedÄquivalent könnte etwa so lauten:

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"

Wenn Ihre GNU sedzu alt ist, um -Eoder zu unterstützen -z, können Sie die erste Zeile ersetzen durch:

sed -r ":1;\$!{N;b1}
Stéphane Chazelas
quelle
Perl-Lösung haben Problem mit Multi-Line: Testen Sie es mit dieser Ausgabe => Echo -e "BEGIN / * Kommentar * / COMMAND / * com \ nment * / END"
بارپابابا
@Babby, funktioniert bei mir. Ich habe einen mehrzeiligen Kommentar und die resultierende Ausgabe in meinem Testfall hinzugefügt.
Stéphane Chazelas
Das Beste, was man heutzutage vergleichen kann, ist gcc -std=c11 -E -P( -ansiist nur ein anderer Name für -std=c90).
zwol
@zwol, die Idee ist, in der Lage zu sein, Code zu verarbeiten, der für einen beliebigen C / C ++ - Standard (c90, c11 oder einen anderen) geschrieben wurde. Streng genommen ist das nicht möglich (siehe mein 2. erfundenes Beispiel). Der Code versucht immer noch, C90-Konstrukte (wie ??') zu handhaben , daher vergleichen wir mit cpp -ansidenen und C99 / C11 ... einem (wie // xxx), daher vergleichen wir mit cpp(oder cpp -std=c11...)
Stéphane Chazelas
@zwol, ich habe den Testfall aufgeteilt, um ein bisschen zu klären. Es sieht so aus, als wären noch Trigraphen in C11, also ist mein zweiter Testfall sowieso nicht Standard C.
Stéphane Chazelas
6

mit sed:

AKTUALISIEREN

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

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

unterstütze alles Mögliche (mehrzeiliger Kommentar, Daten nach [oder und] vorher,);

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

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

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

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

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

e2
-------------------
بارپابابا
quelle
wird für einen Kommentar nicht funktionieren, der nach data beginnt, wieproc print data 2nd /*another comment is here*/
mazs
@mazs aktualisiert, check it
بارپابابا
Dies behandelt keine Kommentare in String-Literalen, was tatsächlich von Bedeutung sein kann, je nachdem, was die SQL macht
zwol
4
 $ cat file | perl -pe 'BEGIN{$/=undef}s!/\*.+?\*/!!sg'

 proc print data=sashelp.cars;
 run;

 data abc;
 set xyz;
 run;

Entfernen Sie leere Zeilen, falls vorhanden:

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

Edit - die kürzere Version von Stephane:

 $ cat file | perl -0777 -pe 's!/\*.*?\*/!!sg'
Hans Schou
quelle
Nun, ich stimme Terdon zu: Sehen wir uns die erwartete Ausgabe an.
Hans Schou
Übrigens: Was soll mit einer einzelnen Zeile geschehen, die Folgendes enthält: "/ * foo * / run; / * bar * /"? Sollte das nur "rennen" sein? ?
Hans Schou
Groß! Dann funktioniert meine Lösung. Hinweis Ich benutze nicht gierig: ". +?"
Hans Schou
2
Sehen Sie -0777als kürzeren WegBEGIN{$/=undef}
Stéphane Chazelas
1
Vielleicht ist .*?statt .+?if auch /**/ein gültiger Kommentar.
Ilkkachu
2

Lösung mit SED-Befehl und ohne Skript

Hier sind Sie ja:

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

Hinweis: Dies funktioniert nicht unter OS X, es sei denn, Sie installieren gnu-sed. Aber es funktioniert unter Linux Distros.

FarazX
quelle
1
Sie können die -iOption verwenden, um die Datei direkt zu bearbeiten, anstatt die Ausgabe in eine neue Datei umzuleiten. oder viel sicherer -i.bakBackup-Datei
Rahul
1
Es funktioniert auch nicht in allen Fällen. Versuchen Sie, einen Kommentar in dieselbe Zeile einzufügen und beobachten Sie, was passiert ... Beispiel set xy \; / * test * / Ich denke, wir werden auch Perl brauchen, um dies auf einfache Weise zu lösen.
Luciano Andress Martini
@ Rahul genau, danke für die Erwähnung. Ich wollte es einfach halten.
FarazX
Es tut mir sehr leid zu sagen, dass es nicht für Kommentare in der gleichen Zeile funktioniert.
Luciano Andress Martini
@LucianoAndressMartini Jetzt ist es soweit!
FarazX
1

sedWird jeweils in einer Zeile ausgeführt, aber einige der Kommentare in der Eingabe erstrecken sich über mehrere Zeilen. Gemäß /unix//a/152389/90751 können Sie zuerst trdie Zeilenumbrüche in ein anderes Zeichen umwandeln. Anschließend sedkönnen Sie die Eingabe als einzelne Zeile verarbeiten und trerneut verwenden, um die Zeilenumbrüche wiederherzustellen.

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

Ich habe Null-Bytes verwendet, aber Sie können jedes Zeichen auswählen, das nicht in Ihrer Eingabedatei enthalten ist.

*Hat in regulären Ausdrücken eine besondere Bedeutung, so dass ein Escapezeichen erforderlich ist, \*um ein Literal zu finden *.

.*ist gierig - es wird mit dem längsten möglichen Text übereinstimmen, einschließlich mehr */und /*. Das bedeutet den ersten Kommentar, den letzten Kommentar und alles dazwischen. Um dies einzuschränken, ersetzen Sie es .*durch ein strengeres Muster: Kommentare können alles enthalten, was kein "*" ist, und auch "*", gefolgt von allem, was kein "/" ist. Läufe von mehreren *s müssen auch berücksichtigt werden:

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

Dadurch werden alle Zeilenumbrüche in den mehrzeiligen Kommentaren entfernt, d. H.

data1 /* multiline
comment */ data2

wird werden

data1  data2

Wenn dies nicht das ist, was gewünscht wurde, sedkann gesagt werden, einen der Zeilenumbrüche beizubehalten. Dies bedeutet, dass Sie ein Zeilenumbruch-Ersetzungszeichen auswählen, das abgeglichen werden kann.

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

Es \fist nicht garantiert, dass das Sonderzeichen und die Verwendung eines Verweises, der möglicherweise nicht mit irgendetwas übereinstimmt, in allen sedImplementierungen wie beabsichtigt funktionieren . (Ich habe bestätigt, dass es unter GNU sed 4.07 und 4.2.2 funktioniert.)

JigglyNaga
quelle
Könnten Sie mir bitte mitteilen, wie es funktioniert. Ich habe es wie folgt versucht. tr '\ n' '\ 0' | sed -e 's, / * ([^ *] \ | * \ + [^ * /]) ** \ + / ,, g' test.sas | tr '\ 0' '\ n' und ich bekam wie folgt: / * Dies ist, um die Ausgabedaten zu drucken * / data abcdf; set cfgtr; Lauf; proc print data = sashelp.cars; Lauf; Daten abc; setze xyz; Lauf;
Sharique Alam
@ShariqueAlam Du hast dort test.sasin die Mitte der Pipeline gestellt, sedliest also direkt daraus und die erste trhat keine Auswirkung. Sie müssen verwendencat test.sas | tr ...
JigglyNaga
0

Verwenden Sie eine Zeile sed, um Kommentare zu entfernen:

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

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
user5337995
quelle