Como posso excluir todos os caracteres em / *… * / incluindo / * & * /?


12

Eu tentei sed e awk, mas não está funcionando, pois o personagem envolve "/", que já está no comando como delimitador.

Por favor, deixe-me saber como posso conseguir isso.

Abaixo está um exemplo de exemplo. Queremos remover as seções comentadas, ou seja, /*.....*/

/*This is to print the output
data*/
proc print data=sashelp.cars;
run;
/*Creating dataset*/
data abc;
set xyz;
run;

-bash-4.1 $ sed ', / *. ** / ,, g' test.sas Abaixo está a saída que recebo, o primeiro comentário ainda está lá. / * Isto é para imprimir os dados de saída * / proc print data = sashelp.cars; corre; abc de dados; definir xyz; corre;
Sharique Alam 21/07

1
Obrigado pela edição. Seria ainda melhor se você incluísse também a saída desejada. Inclua também o que você tentou e como ele falhou na pergunta, não nos comentários.
terdon

2
O que deve acontecer com literais de string contendo comentários ou delimitadores de comentários? (eg INSERT INTO string_table VALUES('/*'), ('*/'), ('/**/');)
zwol

1
Relacionado (desculpe, eu não posso resistir!): Codegolf.stackexchange.com/questions/48326/…
ilkkachu

Atualizei meu post com outras soluções. Verifique novamente se agora é bom para você.
Luciano Andress Martini

Respostas:


22

Eu acho que encontrei uma solução fácil!

cpp -P yourcommentedfile.txt 

ALGUMAS ATUALIZAÇÕES:

Citação do usuário ilkachu (texto original dos comentários do usuário):

Eu brinquei um pouco com as opções do gcc: -fpreprocessed desativará a maioria das diretivas e expansões de macro (exceto #define e #undef aparentemente). Adicionar -dD também deixará define em; e std = c89 pode ser usado para ignorar o novo estilo // comentários. Mesmo com eles, o cpp substitui os comentários por espaços (em vez de removê-los) e recolhe espaços e linhas vazias.

Mas acho que ainda é razoável e uma solução fácil para a maioria dos casos, se você desativar a expansão de macro e outras coisas, acho que obterá bons resultados ... - e sim, você pode combinar isso com o shell script para melhorar ... e muito mais...


1
O uso do pré-processador C é provavelmente a solução mais robusta. Como o pré-processador é provavelmente o analisador mais robusto de comentários em C. Esperto.
21716 grochmal

14
Mas cppvai fazer muito mais do que a remoção de comentários (processo #include, expandir macros, incluindo builtin queridos ...)
Stéphane Chazelas

3
@LucianoAndressMartini, não, tail -n +7apenas removerá as 7 primeiras linhas, não impedirá o #includeprocessamento ou as expansões de macro. Tente echo __LINE__ | cpppor exemplo. Ouecho '#include /dev/zero' | cpp
Stéphane Chazelas

2
Você provavelmente deseja usar o -Pmodo se fizer isso. (Isto pode eliminar a necessidade de utilização tail.)
Zwol

3
Joguei um pouco com as opções do gcc: -fpreprocesseddesativará a maioria das diretivas e expansões de macro (exceto #definee #undefaparentemente). Adicionar -dDtambém deixa define dentro; e std=c89pode ser usado para ignorar novos //comentários de estilo . Mesmo com eles, cppsubstitui os comentários por espaços (em vez de removê-los) e recolhe espaços e linhas vazias.
ilkkachu

10

Certa vez, inventei isso que podemos refinar:

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'

para lidar com mais alguns casos de canto.

Observe que se você remover um comentário, poderá alterar o significado do código ( 1-/* comment */-1é analisado como 1 - -1while 1--1(que você obteria se removesse o comentário) causaria um erro). É melhor substituir o comentário por um caractere de espaço (como fazemos aqui) em vez de removê-lo completamente.

O exemplo acima deve funcionar corretamente nesse código ANSI C válido, por exemplo, que tenta incluir alguns casos de canto:

#include <stdio.h>
int main ()
{
  printf ("% d% s% c% c% c% c% c% s% s% d \ n",
  1 - / * comentário * / - 1,
  / \
* Comente */
  "/ * não é um comentário * /",
  / * multiline
  Comente */
  '"' /* Comente */ , '"',
  '\'','"'/* Comente */,
  '\
\
"', /* Comente */
  "\\
"/ * não é um comentário * /",
  "?? /" / * não é um comentário * / ",
  '??' '+' "'/ *" comentário "* /);
  retornar 0;
}

O que fornece esta saída:

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

  "/ * não é um comentário * /",

  '"', '"'
  '\' ',' "',
  '\
\
"'  
  "\\
"/ * não é um comentário * /",
  "?? /" / * não é um comentário * / ",
  '??' '+' "');
  retornar 0;
}

Ambos imprimem a mesma saída quando compilados e executados.

Você pode comparar com a saída de gcc -ansi -Epara ver o que o pré-processador faria nele. Esse código também é um código C99 ou C11 válido, no entanto, gccdesabilita o suporte a trigrafs por padrão, para que não funcione, a gccmenos que você especifique o padrão como gcc -std=c99ou gcc -std=c11ou adicione a -trigraphsopção).

Também funciona neste código C99 / C11 (não-ANSI / C90):

// Comente
/ \
/ Comente
// multiline \
Comente
"// não é um comentário"

(compare com gcc -E/ gcc -std=c99 -E/ gcc -std=c11 -E)

O ANSI C não suporta o // formcomentário. //não é válido de outra maneira no ANSI C, portanto, não apareceria lá. Um caso artificial em que //pode realmente aparecer no ANSI C (como observado aqui , e você pode achar o restante da discussão interessante) é quando o operador stringify está em uso.

Este é um código ANSI C válido:

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

E na época da discussão em 2004, de gcc -ansi -Efato a expandiu para "//not a comment". No entanto, hoje, gcc-5.4retorna um erro, então duvido que encontraremos muito código C usando esse tipo de construção.

O sedequivalente GNU pode ser algo como:

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 seu GNU sedé muito antigo para suportar -Eou -z, você pode substituir a primeira linha por:

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

solução perl tem problema com múltiplas linha: testá-lo com esta saída => echo -e "BEGIN / * comentário * / COMANDO / * com \ nment * / END"
بارپابابا

@Babby, funciona para mim. Adicionei um comentário de várias linhas e a saída resultante no meu caso de teste.
Stéphane Chazelas

A melhor coisa para comparar com os dias de hoje seria gcc -std=c11 -E -P( -ansié apenas outro nome para -std=c90).
Zwol 21/07

@zwol, a idéia é poder manipular código escrito para qualquer padrão C / C ++ (c90, c11 ou outro). A rigor, não é possível (veja meu segundo exemplo artificial). O código ainda tenta manipular construções C90 (como ??'), portanto, comparamos com cpp -ansiessas e C99 / C11 ... uma (como// xxx ), daí que compara com cpp(ou cpp -std=c11...)
Stéphane Chazelas

@ zwol, eu dividi o caso de teste na tentativa de esclarecer um pouco. Parece que os trigramas ainda estão no C11, então meu segundo caso de teste não é o C padrão.
Stéphane Chazelas

6

com sed:

ATUALIZAR

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

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

suporta todo o possível (comentário em várias linhas, dados após [ou e] antes);

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

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

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

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

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

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

não funcionará para um comentário iniciado após dados, comoproc print data 2nd /*another comment is here*/
mazs 21/07

@mazs atualizado, verifique-o #
222

Isso não lidar com comentários dentro strings literais, que podem realmente importa, dependendo do que o SQL faz
Zwol

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

 proc print data=sashelp.cars;
 run;

 data abc;
 set xyz;
 run;

Remova linhas em branco, se houver:

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

Edit - a versão mais curta de Stephane:

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

bem, concordo com o Terdon: vamos ver a saída esperada.
Hans Schou 21/07

BTW: O que deve acontecer com uma única linha que contém: "/ * foo * / run; / * bar * /"? Isso deveria ser apenas "executado"; ?
Hans Schou 21/07

Ótimo! Então minha solução funciona. Nota Eu uso não ganancioso: ". +?"
Hans Schou 21/07

2
Veja -0777como uma maneira mais curta de fazerBEGIN{$/=undef}
Stéphane Chazelas

1
Talvez em .*?vez de .+?se também /**/seja um comentário válido.
Ilkkachu

2

Solução usando o comando SED e nenhum script

Olha Você aqui:

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

Nota: isso não funciona no OS X, a menos que você instale gnu-sed. Mas funciona em distribuições Linux.


1
você pode usar a -iopção para editar o arquivo no local em vez de redirecionar a saída para o novo arquivo. ou muito mais seguro -i.bakpara arquivo de backup
Rahul

1
Também não está funcionando para todos os casos, tente colocar um comentário na mesma linha e observe o que acontece ... Exemplo set xy \; / * test * / Acho que precisamos do perl também resolver isso de uma maneira fácil.
Luciano Andress Martini

@ Raul exatamente, obrigado por mencionar. Eu só queria manter as coisas mais simples.
21416 FarazX

Lamento dizer que ele não está funcionando para comentários na mesma linha.
Luciano Andress Martini

@LucianoAndressMartini Now it does!
FarazX 21/07

1

sedopera em uma linha por vez, mas alguns dos comentários na entrada abrangem várias linhas. Conforme /unix//a/152389/90751 , você pode primeiro usar trpara transformar as quebras de linha em algum outro caractere. Em seguida, você sedpode processar a entrada como uma única linha e usar trnovamente para restaurar as quebras de linha.

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

Usei bytes nulos, mas você pode escolher qualquer caractere que não apareça no seu arquivo de entrada.

*tem um significado especial em expressões regulares, portanto, precisará ser escapado \*para corresponder a um literal *.

.*é ganancioso - ele corresponderá ao texto mais longo possível, incluindo mais */e /*. Isso significa o primeiro comentário, o último comentário e tudo mais. Para restringir isso, substitua .*por um padrão mais rígido: os comentários podem conter qualquer coisa que não seja um "*" e também "*" seguido por qualquer coisa que não seja um "/". Execuções de múltiplos *s também devem ser consideradas:

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

Isso removerá quaisquer quebras de linha nos comentários de várias linhas, ou seja.

data1 /* multiline
comment */ data2

se tornará

data1  data2

Se não era isso que se queria, sedpode ser dito para manter uma das quebras de linha. Isso significa escolher um caractere de substituição de quebra de linha que possa ser correspondido.

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

O caractere especial \fe o uso de uma referência anterior que pode não corresponder a nada não garantem que funcione conforme o esperado em todas as sedimplementações. (Confirmei que funciona no GNU sed 4.07 e 4.2.2.)


Você poderia por favor me avise como ele vai funcionar. Tentei como abaixo. tr '\ n' '\ 0' | sed -e 's, / * ([^ *] \ | * \ + [^ * /]) ** \ + / ,, g' test.sas | tr '\ 0' '\ n' e obtive o seguinte: / * Isto é para imprimir os dados de saída * / data abcdf; defina cfgtr; corre; proc print data = sashelp.cars; corre; abc de dados; definir xyz; corre;
Sharique Alam

@ShariqueAlam Você colocou test.sasno meio do pipeline lá, então sedlê diretamente, e o primeiro trnão tem efeito. Você precisa usarcat test.sas | tr ...
JigglyNaga 6/06

0

usando uma linha sed para remover comentários:

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

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.