¿Cómo puedo eliminar todos los caracteres incluidos en / * ... * / incluyendo / * & * /?


12

Intenté sed y awk, pero no funciona ya que el carácter implica "/", que ya está al mando como delimitador.

Por favor, hágame saber cómo puedo lograr esto.

A continuación hay un ejemplo de ejemplo. Queremos eliminar las secciones comentadas, es decir /*.....*/

/*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 A continuación se muestra la salida que obtengo, el primer comentario sigue ahí. / * Esto es para imprimir los datos de salida * / proc print data = sashelp.cars; correr; datos abc; establecer xyz; correr;
Sharique Alam

1
Gracias por la edición Sería aún mejor si también incluyeras el resultado deseado. También incluya lo que intentó y cómo falló en la pregunta, no en los comentarios.
terdon

2
¿Qué debería pasar con los literales de cadena que contienen comentarios o delimitadores de comentarios? (por ejemplo INSERT INTO string_table VALUES('/*'), ('*/'), ('/**/');)
zwol

1
Relacionado (lo siento, no puedo resistirme): codegolf.stackexchange.com/questions/48326/…
ilkkachu

Actualicé mi publicación con otras soluciones, vuelva a verificar si ahora es bueno para usted.
Luciano Andress Martini

Respuestas:


22

¡Creo que encontré una solución fácil!

cpp -P yourcommentedfile.txt 

ALGUNAS ACTUALIZACIONES:

Cita del usuario ilkachu (texto original de los comentarios del usuario):

Jugué un poco con las opciones para gcc: -fpreprocessed deshabilitará la mayoría de las directivas y expansiones de macros (excepto #define y #undef aparentemente). Agregar -dD dejará las definiciones también; y std = c89 se puede usar para ignorar el nuevo estilo // comentarios. Incluso con ellos, cpp reemplaza los comentarios con espacios (en lugar de eliminarlos) y contrae espacios y líneas vacías.

Pero creo que sigue siendo razonable y una solución fácil para la mayoría de los casos, si deshabilita la expansión de macros y otras cosas, creo que obtendrá buenos resultados ... y sí, puede combinar eso con el script de shell para mejorar ... y mucho más...


1
El uso del preprocesador C es probablemente la solución más sólida. Dado que el preprocesador es probablemente el analizador de comentarios C más robusto. Inteligente.
grochmal

14
Pero cpphará mucho más que eliminar comentarios (procesar #include, expandir macros, incluidas las incorporadas ...)
Stéphane Chazelas

3
@LucianoAndressMartini, no, tail -n +7solo eliminará las primeras 7 líneas, no evitará el #includeprocesamiento o las expansiones de macros. Prueba echo __LINE__ | cpppor ejemplo. Oecho '#include /dev/zero' | cpp
Stéphane Chazelas

2
Probablemente quieras usar el -Pmodo si haces esto. (Esto puede eliminar la necesidad de usar tail.)
zwol

3
Jugué un poco con las opciones para gcc: -fpreprocesseddeshabilitará la mayoría de las directivas y expansiones de macros (excepto #definey #undefaparentemente). Agregar -dDdejará define también; y std=c89puede usarse para ignorar nuevos //comentarios de estilo . Incluso con ellos, cppreemplaza los comentarios con espacios (en lugar de eliminarlos) y contrae espacios y líneas vacías.
ilkkachu

10

Una vez se me ocurrió esto 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 manejar algunos casos más de esquina.

Tenga en cuenta que si elimina un comentario, podría cambiar el significado del código ( 1-/* comment */-1se analiza como 1 - -1while 1--1(que obtendría si eliminara el comentario) le daría un error). Es mejor reemplazar el comentario con un carácter de espacio (como lo hacemos aquí) en lugar de eliminarlo por completo.

Lo anterior debería funcionar correctamente en este código ANSI C válido, por ejemplo, que intenta incluir algunos casos de esquina:

#include <stdio.h>
int main ()
{
  printf ("% d% s% c% c% c% c% c% s% s% d \ n",
  1 - / * comentario * / - 1,
  / \
* comentario * /
  "/ * no es un comentario * /",
  / * multilínea
  comentario * /
  '"' / * comentario * /, '"',
  '\' ',' "'/ * comentario * /,
  '\
\
"', / * comentario * /
  "\\
"/ * no es un comentario * /",
  "?? /" / * no es un comentario * / ",
  '??' '+' "'/ *" comentario "* /);
  devuelve 0;
}

Lo que da esta salida:

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

  "/ * no es un comentario * /",

  '"', '"',
  '\' ',' "',
  '\
\
"',  
  "\\
"/ * no es un comentario * /",
  "?? /" / * no es un comentario * / ",
  '??' '+' "');
  devuelve 0;
}

Ambos imprimen el mismo resultado cuando se compilan y se ejecutan.

Puede comparar con la salida de gcc -ansi -Epara ver qué haría el preprocesador en él. Ese código es también válido o código C99 C11, sin embargo gccdesactiva trigrafos apoyan de forma predeterminada por lo que no va a funcionar con gccmenos que especifique el estándar como gcc -std=c99o gcc -std=c11o agregar la -trigraphsopción).

También funciona en este código C99 / C11 (no ANSI / C90):

// comentario
/ \
/ comentario
// multilínea \
comentario
"// no es un comentario"

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

ANSI C no apoyó el // formcomentario. //de lo contrario no es válido en ANSI C, por lo que no aparecería allí. Un caso artificial en el que //puede aparecer genuinamente en ANSI C (como se señala allí , y puede encontrar interesante el resto de la discusión) es cuando el operador stringify está en uso.

Este es un código ANSI C válido:

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

Y en el momento de la discusión en 2004, de gcc -ansi -Ehecho lo expandió a "//not a comment". Sin embargo, hoy gcc-5.4devuelve un error, por lo que dudo que encontremos mucho código C con este tipo de construcción.

El sedequivalente de GNU podría 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"

Si su GNU sedes demasiado antigua para admitir -Eo -z, puede reemplazar la primera línea con:

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

la solución perl tiene un problema con la línea múltiple: pruébelo con esta salida => echo -e "BEGIN / * comment * / COMMAND / * com \ nment * / END"
بارپابابا

@Babby, funciona para mí. He agregado un comentario de varias líneas y el resultado resultante en mi caso de prueba.
Stéphane Chazelas

Lo mejor para comparar hoy en día sería gcc -std=c11 -E -P( -ansies solo otro nombre para -std=c90).
zwol

@zwol, la idea es poder manejar el código escrito para cualquier estándar C / C ++ (c90, c11 u otro). Estrictamente hablando, no es posible (ver mi segundo ejemplo artificial). El código todavía trata de manejar construcciones C90 (like ??'), por lo tanto, comparamos con cpp -ansiaquellos y C99 / C11 ... one (like // xxx), por lo tanto, comparamos con cpp(o cpp -std=c11...)
Stéphane Chazelas

@zwol, he dividido el caso de prueba en un intento de aclarar un poco. Parece que los trigrafos todavía están en C11, por lo que mi segundo caso de prueba no es el C estándar de todos modos.
Stéphane Chazelas

6

con sed:

ACTUALIZAR

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

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

admite todo lo posible (comentarios de varias líneas, datos después de [o y] 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
-------------------
correr:
$ sed -f command.sed FILENAME

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

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

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

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

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

no funcionará para un comentario que comience después de los datos, comoproc print data 2nd /*another comment is here*/
mazs

@mazs actualizado, compruébalo
بارپابابا

Esto no maneja los comentarios dentro de los literales de cadena, que en realidad pueden importar, dependiendo de lo que haga el SQL
zwol

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

 proc print data=sashelp.cars;
 run;

 data abc;
 set xyz;
 run;

Elimine las líneas en blanco si las hay:

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

Editar - la versión más corta de Stephane:

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

bueno, estoy de acuerdo con terdon: veamos el resultado esperado.
Hans Schou

Por cierto: ¿Qué debería pasar con una sola línea que contiene: "/ * foo * / run; / * bar * /"? ¿Debería eso ser simplemente "correr"? ?
Hans Schou

¡Excelente! Entonces mi solución funciona. Tenga en cuenta que uso no codicioso: ". +?"
Hans Schou

2
Ver -0777como una forma más corta de hacerloBEGIN{$/=undef}
Stéphane Chazelas

1
Quizás en .*?lugar de .+?si /**/es un comentario válido también.
ilkkachu

2

Solución mediante el comando SED y sin script

Aquí estás:

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

Nota: Esto no funciona en OS X, a menos que lo instale gnu-sed. Pero funciona en Linux Distros.


1
puede usar la -iopción para editar el archivo en el lugar en lugar de redirigir la salida al nuevo archivo. o mucho más seguro -i.bakpara el archivo de respaldo
Rahul

1
No funciona para todos los casos también, intente poner un comentario en la misma línea y observe lo que sucede ... Ejemplo establecido xy \; / * test * / Creo que necesitaremos que Perl también resuelva esto de una manera fácil.
Luciano Andress Martini

@Rahul exactamente, gracias por mencionarlo. Solo quería que fuera más simple.
FarazX

Lamento mucho decir que no funciona para comentarios en la misma línea.
Luciano Andress Martini

@LucianoAndressMartini ¡Ahora sí!
FarazX

1

sedopera en una línea a la vez, pero algunos de los comentarios en la entrada abarcan varias líneas. Según /unix//a/152389/90751 , primero puede usar trpara convertir los saltos de línea en algún otro carácter. Luego sedpuede procesar la entrada como una sola línea, y puede usarla trnuevamente para restaurar los saltos de línea.

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

He usado bytes nulos, pero puede elegir cualquier carácter que no aparezca en su archivo de entrada.

*tiene un significado especial en las expresiones regulares, por lo que necesitará escapar \*para que coincida con un literal *.

.*es codicioso : coincidirá con el texto más largo posible, incluyendo más */y /*. Eso significa el primer comentario, el último comentario y todo lo demás. Para restringir esto, reemplace .*con un patrón más estricto: los comentarios pueden contener cualquier cosa que no sea un "*", y también "*" seguido de cualquier cosa que no sea un "/". Las ejecuciones de múltiples *s también deben tenerse en cuenta:

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

Esto eliminará cualquier salto de línea en los comentarios multilínea, es decir.

data1 /* multiline
comment */ data2

se convertirá

data1  data2

Si esto no es lo que se quería, sedse le puede pedir que mantenga uno de los saltos de línea. Esto significa elegir un personaje de reemplazo de salto de línea que pueda coincidir.

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

\fNo se garantiza que el carácter especial y el uso de una referencia inversa que puede no haber coincidido con nada funcionen como está previsto en todas las sedimplementaciones. (Confirmé que funciona en GNU sed 4.07 y 4.2.2.)


¿Podría decirme cómo funcionará? Intenté lo siguiente. tr '\ n' '\ 0' | sed -e 's, / * ([^ *] \ | * \ + [^ * /]) ** \ + / ,, g' test.sas | tr '\ 0' '\ n' y obtuve lo siguiente: / * Esto es para imprimir los datos de salida * / data abcdf; establecer cfgtr; correr; datos de impresión de proceso = sashelp.cars; correr; datos abc; establecer xyz; correr;
Sharique Alam

@ShariqueAlam Has puesto test.sasen el medio de la tubería allí, por lo que sedlee directamente y el primero trno tiene ningún efecto. Necesitas usarcat test.sas | tr ...
JigglyNaga

0

usando una línea sed para eliminar comentarios:

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

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.