Comment supprimer tous les caractères sous / *… * / y compris / * & * /?


12

J'ai essayé sed et awk, mais ça ne marche pas car le personnage implique "/" qui est déjà là en commande comme délimiteur.

Veuillez me faire savoir comment y parvenir.

Voici un exemple. Nous souhaitons supprimer les sections commentées, c.-à-d. /*.....*/

/*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 Ci-dessous la sortie que j'obtiens, le premier commentaire est toujours là. / * Il s'agit d'imprimer les données de sortie * / proc print data = sashelp.cars; courir; données abc; set xyz; courir;
Sharique Alam

1
Merci pour l'édition. Ce serait encore mieux si vous incluiez également la sortie souhaitée. Incluez également ce que vous avez essayé et comment il a échoué dans la question et non dans les commentaires.
terdon

2
Que doit-il arriver aux chaînes de caractères contenant des commentaires ou des délimiteurs de commentaires? (par exemple INSERT INTO string_table VALUES('/*'), ('*/'), ('/**/');)
zwol

1
Connexes (désolé, je ne peux pas résister!): Codegolf.stackexchange.com/questions/48326/…
ilkkachu

J'ai mis à jour mon message avec d'autres solutions, veuillez revérifier si maintenant c'est bon pour vous.
Luciano Andress Martini

Réponses:


22

Je pense que j'ai trouvé une solution facile!

cpp -P yourcommentedfile.txt 

QUELQUES MISES À JOUR:

Citation de l'utilisateur ilkachu (texte original des commentaires des utilisateurs):

J'ai joué un peu avec les options de gcc: -fpreprocessed désactivera la plupart des directives et extensions de macro (sauf #define et #undef apparemment). L'ajout de -dD laissera également les définitions dans; et std = c89 peut être utilisé pour ignorer le nouveau style // commentaires. Même avec eux, cpp remplace les commentaires par des espaces (au lieu de les supprimer) et réduit les espaces et les lignes vides.

Mais je pense que c'est toujours raisonnable et une solution facile pour la plupart des cas, si vous désactivez l'expansion des macros et d'autres choses, je pense que vous obtiendrez de bons résultats ... - et oui, vous pouvez combiner cela avec un script shell pour aller mieux ... et beaucoup plus...


1
L'utilisation du préprocesseur C est probablement la solution la plus robuste. Étant donné que le préprocesseur est probablement l'analyseur le plus robuste des commentaires C. Intelligent.
grochmal

14
Mais cppfera bien plus que supprimer des commentaires (traiter #include, développer des macros, y compris celles intégrées ...)
Stéphane Chazelas

3
@LucianoAndressMartini, non, tail -n +7supprimera simplement les 7 premières lignes, cela n'empêchera pas le #includetraitement ou les extensions de macro. Essayez echo __LINE__ | cpppar exemple. Ouecho '#include /dev/zero' | cpp
Stéphane Chazelas

2
Vous voudrez probablement utiliser le -Pmode si vous le faites. (Cela peut éliminer la nécessité d'utiliser tail.)
zwol

3
J'ai joué un peu avec les options de gcc: -fpreprocesseddésactivera la plupart des directives et extensions de macro (sauf #defineet #undefapparemment). L'ajout -dDlaissera des définitions aussi; et std=c89peut être utilisé pour ignorer les nouveaux //commentaires de style . Même avec eux, cppremplace les commentaires par des espaces (au lieu de les supprimer) et réduit les espaces et les lignes vides.
ilkkachu

10

Une fois, je suis venu avec ce que nous pouvons affiner:

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'

pour gérer quelques autres cas d'angle.

Notez que si vous supprimez un commentaire, vous pouvez changer la signification du code ( 1-/* comment */-1est analysé comme 1 - -1while 1--1(que vous obtiendriez si vous supprimiez le commentaire) vous donnerait une erreur). Il est préférable de remplacer le commentaire par un caractère espace (comme nous le faisons ici) au lieu de le supprimer complètement.

Ce qui précède devrait fonctionner correctement sur ce code C ANSI valide par exemple qui essaie d'inclure quelques cas d'angle:

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

Ce qui donne cette sortie:

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

  "/ * pas un commentaire * /",

  '"', '"',
  '\' ',' "',
  '\
\
"',  
  "\\
"/ * pas un commentaire * /",
  "?? /" / * pas un commentaire * / ",
  '??' '+' "');
  retourner 0;
}

Les deux affichent la même sortie une fois compilés et exécutés.

Vous pouvez comparer avec la sortie de gcc -ansi -Epour voir ce que ferait le pré-processeur. Ce code est également un code C99 ou C11 valide, mais gccdésactive la prise en charge des trigraphes par défaut, de sorte qu'il ne fonctionnera pas gccsauf si vous spécifiez la norme comme gcc -std=c99ou gcc -std=c11ou ajoutez l' -trigraphsoption).

Il fonctionne également sur ce code C99 / C11 (non-ANSI / C90):

// commentaire
/ \
/ commentaire
// multiligne \
commentaire
"// pas un commentaire"

(comparer avec gcc -E/ gcc -std=c99 -E/ gcc -std=c11 -E)

ANSI C n'a pas soutenu le // formcommentaire. //n'est pas par ailleurs valide dans ANSI C, donc n'y apparaîtrait pas. Un cas artificiel où //peut véritablement apparaître dans ANSI C (comme indiqué ici , et vous pouvez trouver le reste de la discussion intéressant) est lorsque l' opérateur stringify est utilisé.

Il s'agit d'un code ANSI C valide:

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

Et au moment de la discussion en 2004, gcc -ansi -Eil a effectivement été étendu à "//not a comment". Cependant aujourd'hui, gcc-5.4renvoie une erreur dessus, donc je doute que nous trouvions beaucoup de code C en utilisant ce type de construction.

L' sedéquivalent GNU pourrait être quelque chose comme:

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"

Si votre GNU sedest trop ancien pour supporter -Eou -z, vous pouvez remplacer la première ligne par:

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

La solution Perl a un problème avec plusieurs lignes: testez-la avec cette sortie => echo -e "BEGIN / * comment * / COMMAND / * com \ nment * / END"
بارپابابا

@Babby, travaille pour moi. J'ai ajouté un commentaire sur plusieurs lignes et la sortie résultante dans mon cas de test.
Stéphane Chazelas

La meilleure chose à comparer de nos jours serait gcc -std=c11 -E -P( -ansic'est juste un autre nom pour -std=c90).
zwol

@zwol, l'idée est de pouvoir gérer du code écrit pour n'importe quel standard C / C ++ (c90, c11 ou autre). Strictement parlant, ce n'est pas possible (voir mon 2ème exemple artificiel). Le code essaie toujours de gérer les constructions C90 (comme ??'), donc nous comparons avec cpp -ansicelles-ci et C99 / C11 ... une (comme // xxx), donc nous comparons avec cpp(ou cpp -std=c11...)
Stéphane Chazelas

@zwol, j'ai divisé le cas de test pour essayer de clarifier un peu. Il semble que les trigraphes soient toujours en C11, donc mon deuxième cas de test n'est pas du C standard de toute façon.
Stéphane Chazelas

6

avec sed:

MISE À JOUR

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

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

prise en charge de tous les possibles (commentaire sur plusieurs lignes, données après [ou et] avant);

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

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

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

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

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

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

ne fonctionnera pas pour un commentaire commençant après les données, commeproc print data 2nd /*another comment is here*/
mazs

@mazs mis à jour, vérifiez
بارپابابا

Cela ne gère pas les commentaires à l'intérieur des littéraux de chaîne, ce qui peut réellement avoir de l'importance, selon ce que fait le SQL
zwol

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

 proc print data=sashelp.cars;
 run;

 data abc;
 set xyz;
 run;

Supprimez les lignes vides, le cas échéant:

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

Edit - la version courte de Stéphane:

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

eh bien, je suis d'accord avec terdon: permet de voir la sortie attendue.
Hans Schou

BTW: Que doit-il arriver à une seule ligne contenant: "/ * foo * / run; / * bar * /"? Faut-il simplement "exécuter"; ?
Hans Schou

Génial! Ensuite, ma solution fonctionne. Remarque J'utilise non gourmand: ". +?"
Hans Schou

2
Voir -0777comme une façon plus courte de faireBEGIN{$/=undef}
Stéphane Chazelas

1
Peut-être .*?qu'au lieu de .+?si /**/est un commentaire valide aussi.
ilkkachu

2

Solution en utilisant la commande SED et aucun script

Vous voilà:

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

NB Cela ne fonctionne pas sur OS X, sauf si vous installez gnu-sed. Mais cela fonctionne sur Linux Distros.


1
vous pouvez utiliser l' -ioption pour modifier le fichier sur place au lieu de rediriger la sortie vers un nouveau fichier. ou bien plus sûr -i.bakde sauvegarder un fichier
Rahul

1
Cela ne fonctionne pas pour tous les cas aussi, essayez de mettre un commentaire sur la même ligne et regardez ce qui se passe ... Exemple set xy \; / * test * / Je pense que nous aurons également besoin de perl pour résoudre ce problème facilement.
Luciano Andress Martini

@Rahul exactement, merci d'avoir mentionné. Je voulais juste que ce soit plus simple.
FarazX

Je suis vraiment désolé de dire que cela ne fonctionne pas pour les commentaires sur la même ligne.
Luciano Andress Martini

@LucianoAndressMartini Maintenant c'est le cas!
FarazX

1

sedfonctionne sur une seule ligne à la fois, mais certains des commentaires de l'entrée s'étendent sur plusieurs lignes. Selon /unix//a/152389/90751 , vous pouvez d'abord utiliser trpour transformer les sauts de ligne en un autre caractère. Vous sedpouvez ensuite traiter l'entrée comme une seule ligne, et vous utilisez à trnouveau pour restaurer les sauts de ligne.

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

J'ai utilisé des octets nuls, mais vous pouvez choisir n'importe quel caractère qui n'apparaît pas dans votre fichier d'entrée.

*a une signification particulière dans les expressions régulières, il devra donc s'échapper \*pour correspondre à un littéral *.

.*est gourmand - il correspondra au texte le plus long possible, y compris plus */et /*. Cela signifie le premier commentaire, le dernier commentaire et tout le reste. Pour restreindre cela, remplacez-le .*par un modèle plus strict: les commentaires peuvent contenir tout ce qui n'est pas un "*", et aussi "*" suivi de tout ce qui n'est pas un "/". Les séries de plusieurs *s doivent également être prises en compte:

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

Cela supprimera tous les sauts de ligne dans les commentaires multilignes, c'est-à-dire.

data1 /* multiline
comment */ data2

va devenir

data1  data2

Si ce n'est pas ce que vous vouliez, sedon peut vous dire de conserver l'un des sauts de ligne. Cela signifie choisir un caractère de remplacement de saut de ligne qui peut être mis en correspondance.

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

Le caractère spécial \fet l'utilisation d'une référence arrière qui ne correspond peut-être à rien ne sont pas garantis pour fonctionner comme prévu dans toutes les sedimplémentations. (J'ai confirmé que cela fonctionne sur GNU sed 4.07 et 4.2.2.)


Pourriez-vous s'il vous plaît laisser mne savoir comment cela fonctionnera. J'ai essayé comme ci-dessous. tr '\ n' '\ 0' | sed -e, / * ([^ *] \ | * \ + [^ * /]) ** \ + / ,, g 'test.sas | tr '\ 0' '\ n' et j'ai obtenu comme ci-dessous: / * C'est pour imprimer les données de sortie * / data abcdf; set cfgtr; courir; proc print data = sashelp.cars; courir; données abc; set xyz; courir;
Sharique Alam

@ShariqueAlam Vous avez mis test.sasau milieu du pipeline là-bas, alors lisez seddirectement à partir de celui-ci, et le premier trn'a aucun effet. Vous devez utilisercat test.sas | tr ...
JigglyNaga

0

en utilisant une ligne sed pour supprimer les commentaires:

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

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.