• Publicidad

Optimizar transformación de archivos Excel a texto

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

Optimizar transformación de archivos Excel a texto

Notapor MARKO » 2012-01-21 18:19 @805

El archivo que necesito transformar tiene tres hojas (vfp, corriente, datos).

En vfp extraigo las variables $vnominal, $mPT, $pacep y $mpot.
En corriente extraigo las variables $CT, $CENS.
En datos extraigo once columnas de un mínimo de 2880 datos.

La hoja de datos es la que transformo a texto y la columna de fechas las transformo a un formato conveniente.

La hoja de datos está así:

Imagen

Esta conversión la realizo con la ayuda del módulo SpreadSheet::ParseExcel y el archivo de texto resultado contiene dos columnas más uno de numeración y otra con la letra "i".

Adjunto el código que me transforma el Excel a texto y obtiene las variables antes mencionadas.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use Spreadsheet::ParseExcel;
  2. $nombre = "AT911201.xls";
  3.  
  4. sub CONVERSION {
  5.     $a = 0;
  6.     $b = 0;
  7.     $c = 0;
  8.     my $parser = Spreadsheet::ParseExcel->new();
  9.     my $libro  = $parser->parse($nombre);
  10.     if ( !defined $libro ) {
  11.         die $parser->error(), ".\n";
  12.     }
  13.     my $hojanombre;
  14.     my $hoja;
  15.     my $indice;
  16.     my $hojas = $libro->worksheets();
  17.  
  18.     for $indice ( $libro->worksheets() ) {
  19.         $hojanombre = $indice->get_name();
  20.         $hojanombre = lc($hojanombre);
  21.         if ( $hojanombre eq "vfp" ) {
  22.             $hoja = $indice;
  23.             $a    = 1;
  24.             last;
  25.         }
  26.     }
  27.     if ( $a == 1 ) {
  28.         $vnominal = $hoja->get_cell( 1, 3 )->value();    #VARIABLES
  29.         $mPT      = $hoja->get_cell( 2, 3 )->value();
  30.         $pacep    = $hoja->get_cell( 3, 3 )->value();
  31.         $mpot     = $hoja->get_cell( 4, 3 )->value();
  32.     }
  33.     for $indice ( $libro->worksheets() ) {
  34.         $hojanombre = $indice->get_name();
  35.         $hojanombre = lc($hojanombre);
  36.         if ( $hojanombre eq "corriente" ) {
  37.             $hoja = $indice;
  38.             $b    = 1;
  39.             last;
  40.         }
  41.     }
  42.     if ( $b == 1 ) {
  43.         $CT   = $hoja->get_cell( 2, 6 )->value();    #VARIABLES
  44.         $CENS = $hoja->get_cell( 3, 6 )->value();
  45.     }
  46.     for $indice ( $libro->worksheets() ) {
  47.         $hojanombre = $indice->get_name();
  48.         $hojanombre = lc($hojanombre);
  49.         if ( $hojanombre eq "datos" ) {
  50.             $hoja = $indice;
  51.             $c    = 1;
  52.             last;
  53.         }
  54.     }
  55.     if ( $c == 1 ) {
  56.         my ( $minfila, $maxfila ) = $hoja->row_range;
  57.         my ( $mincol,  $maxcol )  = $hoja->col_range;
  58.         my @linea = ();
  59.         my $string;
  60.  
  61.         $contador = 0;
  62.         for my $fila ( $minfila .. $maxfila ) {
  63.             @linea = ();
  64.             for my $columna ( $mincol .. $maxcol ) {
  65.                 my $celda = $hoja->get_cell( $fila, $columna );
  66.                 if ( defined($celda) ) {
  67.  
  68.                     #
  69.                     my $variable = $celda->value();
  70.                     if ( $columna == 0 and $fila > 0 ) {    #CONVERTIMOS EL FORMATO DE FECHA
  71.                         my @cantidades = split( "-", $variable );
  72.                         if ( $cantidades[1] eq "Ene" ) {
  73.                             $cantidades[1] = "01";
  74.                         }
  75.                         if ( $cantidades[1] eq "Feb" ) {
  76.                             $cantidades[1] = "02";
  77.                         }
  78.                         if ( $cantidades[1] eq "Mar" ) {
  79.                             $cantidades[1] = "03";
  80.                         }
  81.                         if ( $cantidades[1] eq "Abr" ) {
  82.                             $cantidades[1] = "04";
  83.                         }
  84.                         if ( $cantidades[1] eq "May" ) {
  85.                             $cantidades[1] = "05";
  86.                         }
  87.                         if ( $cantidades[1] eq "Jun" ) {
  88.                             $cantidades[1] = "06";
  89.                         }
  90.                         if ( $cantidades[1] eq "Jul" ) {
  91.                             $cantidades[1] = "07";
  92.                         }
  93.                         if ( $cantidades[1] eq "Ago" ) {
  94.                             $cantidades[1] = "08";
  95.                         }
  96.                         if ( $cantidades[1] eq "Sep" ) {
  97.                             $cantidades[1] = "09";
  98.                         }
  99.                         if ( $cantidades[1] eq "Oct" ) {
  100.                             $cantidades[1] = "10";
  101.                         }
  102.                         if ( $cantidades[1] eq "Nov" ) {
  103.                             $cantidades[1] = "11";
  104.                         }
  105.                         if ( $cantidades[1] eq "Dic" ) {
  106.                             $cantidades[1] = "12";
  107.                         }
  108.                         my @cantidadess = split( " ", $cantidades[2] );
  109.                         chop( $cantidadess[1] );
  110.                         chop( $cantidadess[1] );
  111.                         chop( $cantidadess[1] );
  112.                         chop( $cantidadess[1] );
  113.                         my $temporal = $cantidadess[0];
  114.                         $cantidadess[0] = $cantidades[0];
  115.                         $cantidades[0]  = $temporal;
  116.                         $cantidades[2]  = join( " ", @cantidadess );
  117.                         $variable = join( "/", @cantidades );
  118.                     }
  119.                     push( @linea, $variable );
  120.  
  121.                     #
  122.                 }
  123.                 else {
  124.                     $celda = "";
  125.                     push( @linea, $celda );
  126.                 }
  127.             }
  128.             my $string = join( "\t", @linea );    #CONSTRUIMOS EL ARCHIVO LINEA A LINEA
  129.             $string = "$contador\t" . "i\t" . $string;
  130.             $nombre =~ s/.xls/.txt/;   #CAMBIAMOS DE EXTENSIÓN .xls-->.txt
  131.             $nombre = ">>" . $nombre;  #SOBREESCRIBIREMOS
  132.             open( ARCHIVO, $nombre ) or die "no se encuentra\n";
  133.             print ARCHIVO "$string\n";
  134.             close ARCHIVO;
  135.             $contador++;
  136.         }
  137.     }
  138. }
  139.  
Coloreado en 0.005 segundos, usando GeSHi 1.0.8.4

El proceso completo se tarda 1 minuto 45 segundos, aproximadamente.

Me pregunto si alguien tiene una idea para optimizar la rutina.

Gracias por adelantado.
Última edición por explorer el 2012-01-21 19:21 @848, editado 2 veces en total
Razón: Formateado de código con Perltidy
MARKO
Perlero nuevo
Perlero nuevo
 
Mensajes: 86
Registrado: 2012-01-10 22:34 @982

Publicidad

Re: Optimizar transformación de archivos Excel a texto

Notapor explorer » 2012-01-21 19:11 @841

En la línea 3 declaras y defines este hash:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my %meses_a_numero = (
  2.     Ene => '01',
  3.     Feb => '02',
  4.     Mar => '03',
  5.     Abr => '04',
  6.     May => '05',
  7.     Jun => '06',
  8.     Jul => '07',
  9.     Ago => '08',
  10.     Sep => '09',
  11.     Oct =>  10,
  12.     Nov =>  11,
  13.     Dic =>  12,
  14. );
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
y luego, las líneas 71 a 117 las cambias por:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.                         my($anno, $mes, $dia, $hora) = $variable =~ /^(\d+)-(\d+)-(\d+) ([\d:]+)/i;
  2.                         if (defined $meses_a_numero{$mes}) {
  3.                             $mes = $meses_a_numero{$mes};
  4.                         }
  5.                         $variable = "$dia/$mes/$anno $hora";
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Más... no es óptimo el abrir y cerrar el fichero continuamente...
  • mueve las líneas 130 a 132 antes del for() de la 62
  • mueve la línea 134 después de la 136
Se pueden hacer más cosas, desde luego...
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: Optimizar transformación de archivos Excel a texto

Notapor MARKO » 2012-01-22 00:43 @072

Te agradezco la respuesta tan pronta, se ve que aun no soy consciente de la potencia de los hash ya que lo que pusiste esta buenísimo.

Más... no es óptimo el abrir y cerrar el fichero continuamente...
mueve las líneas 130 a 132 antes del for() de la 62
mueve la línea 134 después de la 136


¡je,je! Completamente de acuerdo. De hecho en el programa original, estas líneas (130 y 132) no van allí, van en la línea 60 y 61 y de la línea 134 no me había percatado de eso.

Probaré lo del hash.

Te darás cuenta de mi gusto por los ciclos "for anidados" ¡je,je,je!

Gracias.
MARKO
Perlero nuevo
Perlero nuevo
 
Mensajes: 86
Registrado: 2012-01-10 22:34 @982

Re: Optimizar transformación de archivos Excel a texto

Notapor explorer » 2012-01-22 02:27 @143

Aparte de que las banderas $a, $b y $c pueden ser omitidas (ya que bastaría con meter las líneas que afectan en el lugar donde activamos cada bandera), la necesidad de usar bucles anidados es por las limitaciones que te da el módulo Spreadsheet::ParseExcel, ya que solo te da la opción de recuperar una celda cada vez, con el método get_cell().

Yo suelo usar el módulo Spreadsheet::Read, que, aunque pueda parecer lioso al principio, puedes contar luego con funciones como row() o cellrow(), que te devuelven una línea entera cada vez, por lo que los bucles se reducen a uno. Y si, dentro de unos meses, el cliente se cansa de pagar licencias de Micro$oft Office y se pasa a LibreOffice o CSV, me basta con cambiar una línea del programa para que siga funcionando igual.

Otro detalle más cosmético: dar nombres consecuentes a las variables. Eso de tener una variable llamada @cantidades y otra @cantidadess no es nada aconsejable. En cualquier momento te equivocas, y Perl no puede ayudarte aún teniendo el 'strict' y el 'warnings' activado. Incluso a la hora de editar el fichero, el autocompletado de variables se vuelve inútil y lioso. Y no te digo nada cuando vuelvas a leer este código dentro de unos meses.
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: Optimizar transformación de archivos Excel a texto

Notapor MARKO » 2012-01-23 22:34 @982

Gracias explorer, te cuento que mejoró mucho el proceso con el método del hash y la expresión regular :D

En la PC que estoy trabajando (que es viejísima y no tiene casi nada de memoria) el proceso dura 1 minuto 25 segundos.

Al probar el programa en una PC con más memoria el proceso completo (transformación, verificación y creación de informe) se tardó 20 segundos :D

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my($anno, $mes, $dia, $hora) = $variable =~ /^(\d+)-(\d+)-(\d+) ([\d:]+)/i;
  2.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Lo único que deseo comentar es que a esta sintaxis había que cambiarle en el segundo paréntesis una cosa mínima para que funcionara:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my($anno, $mes, $dia, $hora) = $variable =~ /^(\d+)-(\D+)-(\d+) ([\d:]+)/i;
  2.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Lo último que me gustaría preguntarte es si me podrías explicar un poco más ampliamente la sintaxis de la expresión regular; ya entiendo lo de los \d y \D pero no lo de los signos - y + y los corchetes [] y los : me perdió y la opción "i" que está de último.

Gracias por adelantado.
MARKO
Perlero nuevo
Perlero nuevo
 
Mensajes: 86
Registrado: 2012-01-10 22:34 @982

Re: Optimizar transformación de archivos Excel a texto

Notapor explorer » 2012-01-24 10:52 @494

Cierto, tenía que ser \D porque el mes está escrito no con número.

Los caracteres '-' son literales: son los que están separando los componentes de la fecha. No tienen un significado especial: son solo '-'.

Los caracteres '+' sí que tienen un significado para los patrones. Indican 'uno o más' de lo que les precede. Así, \d+ quiere decir 'uno o más dígitos'.

Los corchetes con las 'clases de caracteres'. Son conjuntos de caracteres. Así, [\d:]+ quiere decir que buscamos por 'uno o más caracteres que sean dígitos o ":"'.

La opción /i indica que la correspondencia debería ser independientemente de las mayúsculas y minúsculas (en esta expresión regular no hay caracteres alfabéticos, así que se podría obviar).

Lo interesante de esta expresión regular es el par de paréntesis de captura: solo captura los números y los ':'. En cuanto llega al '-' que separa las milésimas, se detiene y no lo captura, por lo que luego no es necesario hacer los chop() que tenías antes.

Más información en la página de Wikipedia sobre Expresiones regulares, y en perldoc perlre, perldoc perlretut.
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 35 invitados