• Publicidad

Filtrar dos archivos en un array

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Filtrar dos archivos en un array

Notapor pablgonz » 2016-11-28 20:33 @897

Hola a todos, estoy tratando de reducir el siguiente código para adaptarlo dentro de otro script que tengo en funcionamiento. La idea es leer dos archivos de registro de extensión .fls creados durante la ejecución del script en el cual deseo adaptar el código.

Las líneas que me interesan siempre comienzan por OUTPUT, en estas están las rutas de algunos archivos que deseo mover (o eliminar). Aquí dejo dos ejemplos de los archivos de registro:
Sintáxis: (mytest-fig-tmp.fls) [ Descargar ] [ Ocultar ]
  1. PWD /home/pablo/my_scripts/foro_perl 
  2. INPUT /usr/local/texlive/2016/texmf.cnf 
  3. INPUT /usr/local/texlive/2016/texmf-dist/web2c/texmf.cnf 
  4. INPUT /usr/local/texlive/2016/texmf-var/web2c/pdftex/latex.fmt 
  5. INPUT mytest-fig-tmp.tex 
  6. OUTPUT mytest-fig-tmp.log 
  7. INPUT /usr/local/texlive/2016/texmf-dist/tex/latex/base/article.cls 
  8. INPUT /usr/local/texlive/2016/texmf-dist/tex/latex/base/article.cls 
  9. INPUT /usr/local/texlive/2016/texmf-dist/tex/latex/filecontents/filecontents.sty 
  10. INPUT /usr/local/texlive/2016/texmf-dist/tex/latex/filecontents/filecontents.sty 
  11. OUTPUT mytest-fig-tmp.aux 
  12. INPUT /usr/local/texlive/2016/texmf-dist/fonts/map/fontname/texfonts.map 
  13. INPUT /usr/local/texlive/2016/texmf-dist/fonts/tfm/public/cm/cmtt10.tfm 
  14. INPUT pics/content1.tex 
  15. OUTPUT pics/content1.tex 
  16. OUTPUT content2.tex 
  17. OUTPUT mytest-fig-tmp.dvi 
  18. OUTPUT joined2.tex 
  19. INPUT mytest-fig-tmp.aux 
Otro
Sintáxis: (salida.fls) [ Descargar ] [ Ocultar ]
  1. PWD /home/pablo/my_scripts/foro_perl 
  2. INPUT /usr/local/texlive/2016/texmf.cnf 
  3. INPUT /usr/local/texlive/2016/texmf-dist/web2c/texmf.cnf 
  4. INPUT /usr/local/texlive/2016/texmf-var/web2c/pdftex/latex.fmt 
  5. INPUT salida.tex 
  6. OUTPUT salida.log 
  7. INPUT /usr/local/texlive/2016/texmf-dist/tex/latex/base/article.cls 
  8. INPUT /usr/local/texlive/2016/texmf-dist/tex/latex/base/article.cls 
  9. INPUT /usr/local/texlive/2016/texmf-dist/tex/latex/base/size10.clo 
  10. INPUT /usr/local/texlive/2016/texmf-dist/tex/latex/filecontents/filecontents.sty 
  11. OUTPUT salida.aux 
  12. INPUT /usr/local/texlive/2016/texmf-dist/fonts/map/fontname/texfonts.map 
  13. INPUT /usr/local/texlive/2016/texmf-dist/fonts/tfm/public/cm/cmtt10.tfm 
  14. INPUT pics/content1.tex 
  15. OUTPUT pics/content1.tex 
  16. INPUT content2.tex 
  17. OUTPUT content2.tex 
  18. OUTPUT salida.dvi 
  19. INPUT joined2.tex 
  20. OUTPUT joined2.tex 
  21. INPUT salida.aux 

Con el siguiente script logro lo que busco, usando array, greep y algunas operaciones puedo añadir y quitar elementos para luego moverlos, lo he probado en Linux/Windows y funciona:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.22;
  3. use File::Copy;
  4. use Data::Dumper;
  5.  
  6. my $latex  = 1 ; # modo
  7. my $name   = 'mytest'; # nombre entrada
  8. my $output = 'salida' ; # nombre salida
  9. my $ext    = '.tex'; # ext por defecto
  10. my $prefix = 'fig'; # prefijo
  11. my $tmp    ='tmp'; # tmp       
  12. my $tempDir='tmp_out'; # carpeta para guardar
  13.  
  14. ### Quitar duplicados
  15. sub uniq {
  16.     my %seen;
  17.     grep !$seen{$_}++, @_;
  18. }
  19.  
  20. ### Carpeta para guardar los archivos
  21. -e $tempDir or mkdir($tempDir,0744) or die "No puedo crear $tempDir: $!\n";
  22.  
  23. # Archivos protegidos
  24. my @protected = qw();
  25. push(@protected,"$output$ext","$output.pdf");
  26.  
  27. # Las líneas que deseo comienzan por OUTPUT
  28. my $flsline = "OUTPUT";
  29.  
  30. # Añadimos el nombre del archivo .fls
  31. my @flsfile = "$name-$prefix-$tmp.fls";
  32.  
  33. # Agregamos $output.fls (si es que existe)
  34. push(@flsfile,"$output.fls");
  35.  
  36. # Abrimos los dos archivos y filtramos las líneas que inician con OUTPUT
  37. my @tmpfiles;
  38. for my $filename(@flsfile){
  39.     open my $RECtmp, '<', "$filename";
  40.     push @tmpfiles, grep /\Q$flsline/,<$RECtmp>;
  41.     close $RECtmp;
  42. }
  43.  
  44. # Quitamos lo que rodea a OUTPUT y nos quedamos con /../../file
  45. @tmpfiles = grep { s/$flsline\s+//mg } @tmpfiles;
  46. @tmpfiles = grep { s/^\s*|\s*//mg } @tmpfiles;
  47.  
  48. # Si esta en modo latex
  49. if($latex){
  50. push (@tmpfiles,"$name-$prefix-$tmp.ps");
  51. }
  52.  
  53. push(@tmpfiles,@flsfile,"$name-$prefix-$tmp$ext","$name-$prefix-$tmp.pdf");
  54.  
  55. # Quitamos los protegidos de @tmpfiles y lo guardamos en @delfiles
  56. sub array_minus(\@\@) {
  57.         my %e = map{ $_ => undef } @{$_[1]};
  58.         return grep( ! exists( $e{$_} ), @{$_[0]} );
  59. }
  60. my @delfiles = array_minus(@tmpfiles, @protected);
  61.  
  62. print Dumper(@delfiles);
  63.  
  64. foreach my $tmpfile (@delfiles)
  65. {
  66.    move("$tmpfile", "$tempDir");
  67. }
  68. __END__
  69.  
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4
Mi consulta es la siguiente, ¿puedo hacer el código un poco más corto? o escribirlo de otra manera, no abusar de grep, como en las líneas:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. @tmpfiles = grep { s/$flsline\s+//mg } @tmpfiles;
  2. @tmpfiles = grep { s/^\s*|\s*//mg } @tmpfiles;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Creo que tengo algunas líneas de sobra y de seguro se puede hacer más efectivo.

Agradecido de antemano.
Pablo.
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Publicidad

Re: Filtrar dos archivos en un array

Notapor explorer » 2016-12-06 10:53 @495

Pues sí, se puede abreviar algo.

Pero no solo ahí, sino en otras partes del código. Por ejemplo, hay varios sitios donde tenemos "$variable" que realmente puede dejarse como $variable. O quitar paréntesis que no son necesarios. Todo eso hace aumentar el nivel de pelusa de un programa.

Los grep{}, por otra parte, los estás usando, no para filtrar elementos, que es su labor principal, sino para realizar una operación de limpieza sobre los textos leídos, y eso se puede hacer con un bucle for() y el operador de sustitución.

También... la subrutina uniq() se quita porque no se usa. Y la subrutina array_minus() se podría quitar si estamos seguros que ningún archivo de los que contiene @protected aparecerá dentro de los archivos fls, en las líneas OUTPUT.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.22;
  3. use File::Copy;
  4. use Data::Dumper;
  5.  
  6. my $latex   = 1;                # modo
  7. my $name    = 'mytest';         # nombre entrada       
  8. my $output  = 'salida';         # nombre salida
  9. my $ext     = 'tex';            # ext por defecto
  10. my $prefix  = 'fig';            # prefijo
  11. my $tempDir = 'tmp_out';        # carpeta para guardar
  12.  
  13. ### Carpeta para guardar los archivos
  14. -e $tempDir or mkdir($tempDir,0744) or die "No puedo crear $tempDir: $!\n";
  15.  
  16. # Archivos protegidos
  17. my @protected = ("$output.$ext", "$output.pdf");
  18.  
  19. # Las líneas que deseo comienzan por OUTPUT
  20. my $flsline = "OUTPUT";
  21.  
  22. # Añadimos el nombre del archivo .fls
  23. my @flsfile = ("$name-$prefix-tmp.fls", "$output.fls");
  24.  
  25. # Abrimos los dos archivos y filtramos las líneas que inician con OUTPUT
  26. my @tmpfiles;
  27. for my $filename (@flsfile){
  28.     open my $REC, '<', $filename;
  29.     push @tmpfiles, grep /^$flsline/, <$REC>;
  30.     close   $REC;
  31. }
  32.  
  33. # Quitamos lo que rodea a OUTPUT y nos quedamos con /../../file
  34. foreach (@tmpfiles) {
  35.     s/^$flsline\s+|\s+$//g;
  36. }
  37.  
  38. # Si está en modo latex
  39. if ($latex) {
  40.     push @tmpfiles, "$name-$prefix-tmp.ps";
  41. }
  42.  
  43. push @tmpfiles, @flsfile, "$name-$prefix-tmp.$ext", "$name-$prefix-tmp.pdf";
  44.  
  45. # Quitamos los protegidos de @tmpfiles y lo guardamos en @delfiles
  46. sub array_minus(\@\@) {
  47.         my %e = map{ $_ => undef } @{$_[1]};
  48.         return grep( ! exists( $e{$_} ), @{$_[0]} );
  49. }
  50.  
  51. my @delfiles = array_minus(@tmpfiles, @protected);
  52.  
  53. print Dumper(@delfiles);
  54.  
  55. foreach my $tmpfile (@delfiles) {
  56.    move($tmpfile, $tempDir);
  57. }
  58.  
  59. __END__
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Filtrar dos archivos en un array

Notapor pablgonz » 2016-12-06 22:40 @986

Muchas gracias por la respuesta explorer, la verdad es que tengo el mal hábito de colocar comillas dobles a casi todo (bash :( ), con lo de los paréntesis sobrantes siempre me pierdo, en algunas ocasiones son necesarios y en otras no, muchas veces modifico código que es parte o adaptación de soluciones que están en este mismo foro o en la red y no tengo un ojo tan prolijo al minuto de escribir y tratar de "optimizar y depurar", como esa subrutina que no tenía nada que hacer ahí.

Lo de cambiar por greep por foreach era más menos lo que había procesado al ver el uso que le dan (en general), supuse que era como un F16 para matar una mosca :). Con esas líneas que propones, modificaré varias partes del script principal en el cual trabajo.
Agradecido por todo.
Pablo
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Re: Filtrar dos archivos en un array

Notapor explorer » 2016-12-07 21:13 @926

No pasa nada por dejar las comillas. Seguro que Perl lo optimizará al compilar el programa. Pero es más fácil de leerlo si quitamos caracteres superfluos.

En el tema de los paréntesis la regla es: se pueden quitar siempre y cuando no afecten a las operaciones que les rodean. Por eso, es mucho más fácil quitar los paréntesis del final de una sentencia. Ejemplo:

my $var = 10 + int rand 100;

En esa línea, tanto int como rand deberían llevar paréntesis, pero como la evaluación comienza en rand(100), y no hay nada más que le siga (la línea acaba en el punto y coma), pues quitamos los paréntesis. Y lo mismo podríamos decir de int(). Cosa muy distinta es si lo ponemos así:

my $var = 10 + int 100 * rand;

¿Estamos ejecutando rand() y luego lo multiplicamos por 100 y luego nos quedamos con el valor entero, o primera calcula el valor entero de 100 y luego lo multiplica por rand()? Solución: es el primera caso, pero no es obvio (habría que consultar la tabla de precedencias). En estos casos, ponemos paréntesis, y listo.

A nivel de operaciones, lo que tenías dentro del grep{} ocupa el mismo número que las que ejecuta el for(). La diferencia es que grep "deja pasar" los valores que son positivos, que es algo que no nos interesa ahora. Solo queremos filtrar contenidos, no escoger unos de otros. Por eso es más apropiado, para esos casos, usar map{}, o un for().
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 6 invitados