Come posso eliminare tutti i caratteri che rientrano in / * ... * / compresi / * & * /?


12

Ho provato sed e awk, ma non funziona perché il personaggio coinvolge "/" che è già lì al comando come delimitatore.

Per favore fatemi sapere come posso raggiungere questo obiettivo.

Di seguito è riportato un esempio.Vogliamo rimuovere le sezioni commentate, ad es /*.....*/

/*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 Di seguito è riportato l'output che ottengo, il primo commento è ancora lì. / * Questo serve per stampare i dati di output * / proc print data = sashelp.cars; correre; dati abc; impostare xyz; correre;
Sharique Alam,

1
Grazie per la modifica. Sarebbe ancora meglio se includessi anche l'output desiderato. Includi anche cosa hai provato e come è fallito nella domanda non nei commenti.
terdon

2
Cosa dovrebbe accadere ai valori letterali di stringa contenenti commenti o delimitatori di commenti? (ad es. INSERT INTO string_table VALUES('/*'), ('*/'), ('/**/');)
zwol,

1
Correlati (scusate non posso resistere!): Codegolf.stackexchange.com/questions/48326/…
ilkkachu,

Ho aggiornato il mio post con un'altra soluzione, per favore ricontrolla se ora è buono per te.
Luciano Andress Martini,

Risposte:


22

Penso di aver trovato una soluzione semplice!

cpp -P yourcommentedfile.txt 

ALCUNI AGGIORNAMENTI:

Citazione dell'utente ilkachu (testo originale dei commenti dell'utente):

Ho giocato un po 'con le opzioni per gcc: -fpreprocessed disabiliterà la maggior parte delle direttive e delle espansioni di macro (apparentemente tranne #define e #undef). L'aggiunta di -dD lascerà anche le definizioni; e std = c89 può essere usato per ignorare il nuovo stile // commenti. Anche con loro, cpp sostituisce i commenti con spazi (invece di rimuoverli) e comprime spazi e righe vuote.

Ma penso che sia ancora ragionevole e una soluzione facile per la maggior parte dei casi, se disabiliti l'espansione macro e altre cose penso che otterrai risultati buoni ... - e sì puoi combinarlo con lo script della shell per migliorare ... e altro ancora...


1
L'uso del preprocessore C è probabilmente la soluzione più affidabile. Poiché il preprocessore è probabilmente il parser più robusto di commenti C. Intelligente.
Grochmal

14
Ma cppfarà molto di più che rimuovere i commenti (elabora #include, espandi macro, comprese quelle incorporate ...)
Stéphane Chazelas,

3
@LucianoAndressMartini, no, tail -n +7rimuoverà solo le prime 7 righe, non impedirà l' #includeelaborazione o le espansioni macro. Prova echo __LINE__ | cppad esempio. Oppureecho '#include /dev/zero' | cpp
Stéphane Chazelas,

2
Probabilmente vuoi usare la -Pmodalità se lo fai. (Questo può eliminare la necessità di utilizzare tail.)
zwol

3
Ho giocato un po 'con le opzioni per gcc: -fpreprocesseddisabiliterò la maggior parte delle direttive e delle macro espansioni (tranne #definee #undefapparentemente). L'aggiunta -dDlascerà anche le definizioni; e std=c89può essere utilizzato per ignorare i nuovi //commenti di stile . Anche con loro, cppsostituisce i commenti con spazi (anziché rimuoverli) e comprime spazi e linee vuote.
ilkkachu,

10

Una volta ho pensato a questo che possiamo affinare a:

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'

per gestire alcuni altri casi angolari.

Si noti che se si rimuove un commento, è possibile modificare il significato del codice ( 1-/* comment */-1analizzato come 1 - -1while 1--1(che si otterrebbe se si rimuovesse il commento) si darebbe un errore). È meglio sostituire il commento con un carattere spaziale (come facciamo qui) invece di rimuoverlo completamente.

Quanto sopra dovrebbe funzionare correttamente su questo codice ANSI C valido, ad esempio, che tenta di includere alcuni casi angolari:

#include <stdio.h>
int main ()
{
  printf ("% d% s% c% c% c% c% c% s% s% d \ n",
  1 - / * commento * / - 1,
  / \
* commento * /
  "/ * non un commento * /",
  / * multilinea
  commento * /
  '"' / * comment * /, '"',
  '\' ',' "'/ * comment * /,
  '\
\
"', / * commento * /
  "\\
"/ * non un commento * /",
  "?? /" / * non un commento * / ",
  '??' '+' "'/ *" comment "* /);
  ritorna 0;
}

Che dà questo risultato:

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

  "/ * non un commento * /",

  '"', '"',
  '\' ',' "',
  '\
\
"',  
  "\\
"/ * non un commento * /",
  "?? /" / * non un commento * / ",
  '??' '+' "');
  ritorna 0;
}

Entrambi stampano lo stesso output quando vengono compilati ed eseguiti.

Puoi confrontare con l'output di gcc -ansi -Eper vedere cosa farebbe il pre-processore su di esso. Tale codice è anche un codice C99 o C11 valido, tuttavia gccdisabilita il supporto trigraph per impostazione predefinita, quindi non funzionerà a gccmeno che non si specifichi lo standard gcc -std=c99o gcc -std=c11o si aggiunga l' -trigraphsopzione).

Funziona anche con questo codice C99 / C11 (non ANSI / C90):

// commento
/ \
/ commento
// multilinea \
commento
"// non un commento"

(confronta con gcc -E/ gcc -std=c99 -E/ gcc -std=c11 -E)

ANSI C non supportava il // formcommento. //non è altrimenti valido in ANSI C quindi non apparirebbe lì. Un caso forzato in cui //potrebbe effettivamente apparire in ANSI C (come notato qui , e potresti trovare interessante il resto della discussione) è quando l' operatore stringify è in uso.

Questo è un codice ANSI C valido:

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

E al momento della discussione del 2004, l' gcc -ansi -Eaveva davvero ampliata "//not a comment". Tuttavia oggi gcc-5.4restituisce un errore, quindi dubito che troveremo molto codice C usando questo tipo di costrutto.

L' sedequivalente GNU potrebbe essere qualcosa del tipo:

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"

Se la tua GNU sedè troppo vecchia per supportare -Eo -z, puoi sostituire la prima riga con:

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

la soluzione perl ha un problema con la linea multipla: testala con questo output => echo -e "BEGIN / * comment * / COMMAND / * com \ nment * / END"
بارپابابا

@Babby, funziona per me. Ho aggiunto un commento su più righe e l'output risultante nel mio caso di test.
Stéphane Chazelas,

La cosa migliore da confrontare al giorno d'oggi sarebbe gcc -std=c11 -E -P( -ansiè solo un altro nome per -std=c90).
zwol,

@zwol, l'idea è quella di essere in grado di gestire il codice scritto per qualsiasi standard C / C ++ (c90, c11 o altro). A rigor di termini, non è possibile (vedi il mio secondo esempio inventato). Il codice cerca ancora di gestire costrutti C90 (come ??'), quindi confrontiamo con cpp -ansiquelli e C99 / C11 ... uno (come // xxx), quindi confrontiamo con cpp(o cpp -std=c11...)
Stéphane Chazelas,

@zwol, ho diviso il test case nel tentativo di chiarire un po '. Sembra che le trigrafi siano ancora in C11, quindi il mio secondo caso di test non è C standard comunque.
Stéphane Chazelas,

6

con sed:

AGGIORNARE

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

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

supportare tutto il possibile (commento su più righe, dati dopo [o e] prima);

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

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

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

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

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

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

non funzionerà per un commento che inizia dopo i dati, comeproc print data 2nd /*another comment is here*/
mazs

@mazs aggiornato, controlla
بارپابابا

Questo non gestisce i commenti all'interno dei valori letterali delle stringhe, il che può effettivamente importare, a seconda di ciò che fa l'SQL
zwol

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

 proc print data=sashelp.cars;
 run;

 data abc;
 set xyz;
 run;

Rimuovi eventuali righe vuote:

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

Modifica: la versione più breve di Stephane:

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

bene, sono d'accordo con terdon: vediamo l'output previsto.
Hans Schou,

A proposito: cosa dovrebbe accadere a una singola riga contenente: "/ * foo * / run; / * bar * /"? Dovrebbe essere solo "corsa"; ?
Hans Schou,

Grande! Quindi la mia soluzione funziona. Nota che uso non avido: ". +?"
Hans Schou,

2
Vedi -0777come un modo più breve di fareBEGIN{$/=undef}
Stéphane Chazelas,

1
Forse .*?invece di .+?se /**/è anche un commento valido.
ilkkachu,

2

Soluzione utilizzando il comando SED e nessuno script

Ecco qui:

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

NB Questo non funziona su OS X, a meno che non venga installato gnu-sed. Ma funziona su Linux Distros.


1
è possibile utilizzare l' -iopzione per modificare il file sul posto anziché reindirizzare l'output su un nuovo file. o molto più sicuro -i.bakal file di backup
Rahul

1
Non funziona anche per tutti i casi, prova a inserire un commento nella stessa riga e guarda cosa succede ... Esempio set xy \; / * test * / Penso che avremo bisogno che anche Perl risolva questo problema in modo semplice.
Luciano Andress Martini,

@Rahul esattamente, grazie per averlo menzionato. Volevo solo renderlo più semplice.
FarazX,

Mi dispiace molto dire che non funziona per i commenti nella stessa riga.
Luciano Andress Martini,

@LucianoAndressMartini Ora lo fa!
FarazX,

1

sedopera su una riga alla volta, ma alcuni dei commenti nell'input si estendono su più righe. Come da /unix//a/152389/90751 , puoi prima usare trper trasformare le interruzioni di riga in qualche altro personaggio. Quindi è sedpossibile elaborare l'input come una riga singola e trriutilizzarlo per ripristinare le interruzioni di riga.

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

Ho usato byte nulli, ma puoi scegliere qualsiasi carattere che non appare nel tuo file di input.

*ha un significato speciale nelle espressioni regolari, quindi dovrà sfuggire \*per corrispondere a un letterale *.

.*è avido - corrisponderà al testo più lungo possibile, incluso più */e /*. Ciò significa che il primo commento, l'ultimo commento e tutto il resto. Per limitarlo, sostituiscilo .*con uno schema più rigoroso: i commenti possono contenere tutto ciò che non è un "*" e anche "*" seguito da tutto ciò che non è un "/". Le esecuzioni di più messaggi *devono anche essere contabilizzate:

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

Ciò rimuoverà qualsiasi interruzione di riga nei commenti su più righe, ad es.

data1 /* multiline
comment */ data2

diventerà

data1  data2

Se questo non è ciò che si voleva, si sedpuò dire di mantenere una delle interruzioni di riga. Questo significa scegliere un carattere sostitutivo per l'interruzione di riga che può essere abbinato.

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

Il carattere speciale \fe l'uso di un riferimento indietro che potrebbe non corrispondere a nulla, non sono garantiti per funzionare come previsto in tutte le sedimplementazioni. (Ho confermato che funziona su GNU sed 4.07 e 4.2.2.)


Potresti per favore farmi sapere come funzionerà. Ho provato come di seguito. tr '\ n' '\ 0' | sed -e 's, / * ([^ *] \ | * \ + [^ * /]) ** \ + / ,, g' test.sas | tr '\ 0' '\ n' e ho ottenuto come di seguito: / * Questo è per stampare i dati di output * / data abcdf; impostare cfgtr; correre; proc print data = sashelp.cars; correre; dati abc; impostare xyz; correre;
Sharique Alam,

@ShariqueAlam Ci hai messo test.sasal centro della pipeline lì, quindi sedlegge direttamente da essa e il primo trnon ha alcun effetto. Devi usarecat test.sas | tr ...
JigglyNaga il

0

usando una riga sed per rimuovere i commenti:

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

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.