Como apagar comentários vazios com sed?

2

Eu quero excluir comentários vazios de Java como os seguintes:

/**
*/

/*
 *
 *
 *
*/

Eu tentei com sed, mas o seguinte exclui todos os comentários, não apenas os vazios:

sed -r "/^\s*\/\*+\s*/,/\s*\*+\/\s*/d"

Como posso dizer ao sed para excluir apenas intervalos com linhas da forma \s\*+\s entre? Eu olhei para link mas não encontrei a solução lá.

P.S .: criei o seguinte arquivo de teste:

/**
bla bla bla
*/

/*ba dff
 *dd fdf
 *d f
 *df df df
*/

/**
*/

/*
 *
 *
 *
*/

class Test
{
 some code...
}

A primeira abordagem:

sed -n '/[^ \/\*]/p' /tmp/tmp

 bla bla bla
/*ba dff
 *dd fdf
 *d f
 *df df df
class Test
{
 some code...
}

Um dos comentários tem sua introdução e finalização eliminados.

perl -0777 -pe 's,\s*/\*[*\s]*\*/\s*, ,gs' < /tmp/tmp

/**
bla bla bla
*/

/*ba dff
 *dd fdf
 *d f
 *df df df
*/  class Test
{
 some code...
}

O último comentário está na mesma linha que a definição de classe.

abordagem de warl0ck:

$ removeemptycomments /tmp/tmp








class Test
{
 some code...
}
    
por Konrad Höffner 22.10.2012 / 13:33

5 respostas

3

sed funciona em uma linha por vez. Use perl em vez disso:

perl -0777 -pe 's,\s*/\*[*\s]*\*/\s*, ,gs'

Isso converterá um comentário vazio e todos os espaços ao redor dele em um único espaço (necessário, caso contrário, int/* */i seria transformado em inti caso contrário, por exemplo). Agora, isso significa que, se você tiver comentários vazios consecutivos, terá espaços em branco consecutivos, portanto, poderá alterá-lo para:

perl -0777 -pe 's,\s*(?:/\*[*\s]*\*/\s*)+, ,gs'

Agora, para preservar o espaçamento, depende do que você deseja fazer e de como os comentários são exibidos. Se os comentários estiverem em toda parte e você quiser preservar o recuo após um comentário, mas ainda quiser condensar espaço desnecessário, tente:

perl -0777 -pe 's,(\s*)(?:\s*/\*[*\s]*\*/)+(\s*\n|\s*),"$1$2"=~/\n/?"\n":" ",ges'

Ou seja, se houver novas linhas em torno dos comentários, substitua por uma nova linha (e o recuo original após uma nova linha após os comentários) ou um espaço diferente.

Isso é possível com o perl porque o operador de alternância regexp perl é contrário a outro mecanismo de expressão regular, não tente encontrar a correspondência mais longa, mas observe cada parte da alternação, por sua vez, até corresponder a uma.

Agora, se você quiser ignorar os comentários vazios que podem ocorrer dentro de strings (como "/***/" ), ele se torna um pouco mais complicado, especialmente quando você precisa levar em consideração aspas duplas ou aspas duplas entre aspas simples. Mas, mais uma vez, o operador regexp de alternância perl vem para o resgate:

perl -0777 -pe 's,(\s*)(?:\s*/\*[*\s]*\*/)+(\s*\n|\s*)|(/\*.*?\*/|//.*?\n|"(?:\.|.)*?"|'\''(?:\.)?.*?'\''|.[^"/'\'']*),"$3"or"$1$2"=~/\n/?"\n":" ",ges'

A ideia é que esse regexp corresponda ao arquivo inteiro, mas em diferentes alternativas dessa grande alternação que funciona um pouco como um tokeniser .

Basicamente, ele percorre o arquivo e o divide em tokens. O texto é considerado como uma sequência de tokens que são os comentários vazios que estamos procurando, ou strings com aspas duplas ou caracteres com aspas simples (possivelmente com escape como '\'' ou '3' ) ou qualquer outra coisa.

Tokens que são os comentários vazios que estamos procurando são substituídos por espaço ou nova linha como acima, qualquer outra coisa é substituída por si mesma. Então, agora deve ser capaz de processar entradas como

/* comments with " unmatched quotes ' */ /* */
  f('"', "/***/" /***/, "\"", "/****/")

corretamente. Agora, não estando familiarizado com a sintaxe java , eu suponho que pode haver casos difíceis em que isso não funcione, então um especialista em java seria capaz de melhorá-lo (por exemplo, em C, você precisaria responder por trigraphs ou barras invertidas podem ser usados para escapar de novas linhas, por isso perderia um /* que foi quebrado no meio como /\<LF>* , pode ser o mesmo em java, então você poderia melhorar esse código para levar isso em conta) .

    
por 22.10.2012 / 13:51
0

Tentando imprimir o esperado em vez de excluir, o que é mais simples neste caso:

sed -n '/[^ \/\*]/p' file

Isso tenta imprimir todas as linhas que contêm algo diferente de /, espaço ou um *

    
por 22.10.2012 / 13:39
0
sed '/\/\*/{:a;N;/\*\//!ba};/^\s*\/\*\+[*\s\n ]*\*\/\s*$/d'

onde

/\/\*/{:a;N;/\*\//!ba}

anexará todos os comentários em uma

/^\s*\/\*\+[*\s\n ]*\*\/\s*$/d

irá verificar se está vazio comentário e apagá-lo se for.

    
por 22.10.2012 / 14:04
0

Aqui está algo que encontrei na internet há alguns anos, funciona para várias linhas,

#!/bin/sed -f
# if no /* get next
/\/\*/!b
# here we've got an /*, append lines until get the corresponding
# */
:x
/\*\//!{
N
bx
}
# delete /*...*/
s/\/\*.*\*\///
    
por 22.10.2012 / 13:58
0

Embora isso seja possível com o sed, é complicado reconhecer comentários de várias linhas, especialmente se você quiser ser cuidadoso e manipular corretamente /* ou */ dentro de uma string literal ou um comentário // .

Os arquivos de origem normalmente são pequenos o suficiente para caber completamente na memória por uma grande margem, portanto, não há nenhum benefício em processá-los linha por linha. Carregue o arquivo inteiro na memória com uma linguagem como Perl e Python e faça uma tokenização parcial. Aqui está um programa Perl minimamente não testado.

perl -0777 -ne '
    while ($_ ne "") {
        if (s~\A[^/"]+|\A\x27\?.\x27|\A"(?:[^\"]|\.")~~) { print $&; } # not a comment
        elsif (s~\A//(.*)$~~m) { $c = $&; print $c if $1 =~ /\S/ } # // comment
        elsif (s~\A/\*(.*?)(\*/|\z)~~) { # /*comment*/
            $c = $&;
            if ($1 =~ /\A[^\n\t *]/ || !$2) {
                print $c; # non-empty or non-terminated comment
            } else {
                $c =~ s/[^\n]//g; # empty comment: retain the newlines
                print $c;
            }
        } else {s~\A.~~; print $&;}
    }

'

    
por 23.10.2012 / 01:35

Tags