Perl en Español

  1. Home
  2. Tutoriales
  3. Foro
  4. Artículos
  5. Donativos
  6. Publicidad
 
Índice general » Mundo Perl » Básico » Optimizar transformación de archivos Excel a texto  RESUELTO Responder al tema
Nuevo tema


Página 1 de 1  [ 6 mensajes ] 
 
Nota 2012-01-21 18:19 @805

Perlero Nuevo
Registrado: 2012-01-10 22:34 @982
Mensajes: 23
Optimizar transformación de archivos Excel a texto
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.
Syntax: [ Download ] [ Hide ]
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.  

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
Formateado de código con Perltidy


Nota 2012-01-21 19:11 @841
Avatar de Usuario
Administrador
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España
Mensajes: 10268
Re: Optimizar transformación de archivos Excel a texto
En la línea 3 declaras y defines este hash:
Syntax: [ Download ] [ Hide ]
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. );
y luego, las líneas 71 a 117 las cambias por:
Syntax: [ Download ] [ Hide ]
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";

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


Nota 2012-01-22 00:43 @072

Perlero Nuevo
Registrado: 2012-01-10 22:34 @982
Mensajes: 23
Re: Optimizar transformación de archivos Excel a texto
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.

Citar:
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.


Nota 2012-01-22 02:27 @143
Avatar de Usuario
Administrador
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España
Mensajes: 10268
Re: Optimizar transformación de archivos Excel a texto
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


Nota 2012-01-23 22:34 @982

Perlero Nuevo
Registrado: 2012-01-10 22:34 @982
Mensajes: 23
Re: Optimizar transformación de archivos Excel a texto
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

Syntax: [ Download ] [ Hide ]
Using perl Syntax Highlighting
  1. my($anno, $mes, $dia, $hora) = $variable =~ /^(\d+)-(\d+)-(\d+) ([\d:]+)/i;
  2.  

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:
Syntax: [ Download ] [ Hide ]
Using perl Syntax Highlighting
  1. my($anno, $mes, $dia, $hora) = $variable =~ /^(\d+)-(\D+)-(\d+) ([\d:]+)/i;
  2.  


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.


Nota 2012-01-24 10:52 @494
Avatar de Usuario
Administrador
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España
Mensajes: 10268
Re: Optimizar transformación de archivos Excel a texto  RESUELTO
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


Responder al tema  [ 6 mensajes ] 

Reglas del Foro
No puedes abrir nuevos temas en este Foro
No puedes responder a temas en este Foro
No puedes editar tus mensajes en este Foro
No puedes borrar tus mensajes en este Foro
No puedes enviar adjuntos en este Foro

Publicidad

Socializa

Síguenos por Twitter

Suscríbete GRATUITAMENTE al Boletín de Perl en Español

Saltar a:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Traducción al español por Huan Manwë para phpbb-es.com
phpBB SEO