A menudo me ha tocado intercambiar datos con colegas en formato
Excel y uno de los problemas que he padecido es el de tener que ir juntando a mano diferentes conjuntos de datos en diferentes hojas del mismo libro de cálculo. Una molestia añadida es la necesidad de importar archivos en formato texto, como
columnas separadas por tabuladores, por ejemplo, en el mismo libro. Para automatizar este tipo de tareas y usar Excel/OpenOffice realmente sólo para el análisis de los datos, podemos echar mano por ejemplo del módulo
Spreadsheet del CPAN. El mismo módulo puede servir además para leer y procesar datos directamente desde archivos Excel.
Volviendo al problema descrito arriba, supongamos por ejemplo que queremos unir en un sólo archivo excel tres archivos (
datos1.xls, datos2.xls y
datos3.tab) en uno solo que llamaremos
todos_datos.xls . Pues bien, por medio del programa descrito debajo sería tan sencillo como ejecutar en el terminal:
$ ./my_xls_merge.pl todos_datos.xls datos1.xls datos2.xls datos3.tab
Yo lo he probado en Linux y los archivos generados se leen perfectamente con el MS Excel, con la única pega de que se pierden las gráficas que hubiera ya en los archivos de entrada.
El código es el siguiente:
#!/usr/bin/perl -w
# programa 9 my_xls_merge.pl
# programita para unir hojas de diferentes archivos .xls o .tab en un solo libro excel
# OJO: conserva el valor de cada celda de una hoja de calculo, no las formulas,
# y se pierden los diagramas por ejemplo
# inspirado por http://www.perlmonks.org/?node_id=743574
use strict;
use Spreadsheet::ParseExcel;
use Spreadsheet::WriteExcel;
use File::Basename;
my $MAXLENGTHSHEETNAME = 31;
die "# usage: $0 <outfile.xls> <file1.xls> ... <fileN.xls>\n" if(scalar(@ARGV) < 2);
my @infiles = @ARGV;
my $outfile = shift(@infiles);
if(-e $outfile)
{
print "# a file named '$outfile' already exists, overwrite? (Y/N)\n";
my $overwrite = <STDIN>;
if($overwrite ne "Y\n"){ exit }
else{ unlink($outfile) }
}
# instantiate resulting excel object
my $outbook = Spreadsheet::WriteExcel->new($outfile) ||
die "# $0: cannot create '$outfile': $!";
for my $file (@infiles)
{
if(!-e $file)
{
print "# skipping non-existing file '$file'\n";
next;
}
# remove path
my ($filename,$copysheet_name) = (fileparse($file));
my $excel = Spreadsheet::ParseExcel::Workbook->Parse($file);
if(defined($excel->{'File'})) # excel format infiles
{
foreach my $sheet (@{$excel->{Worksheet}})
{
$copysheet_name = $sheet->{'Name'} . '-' . $filename;
if(length($copysheet_name) > $MAXLENGTHSHEETNAME)
{
print "# abbreviating $copysheet_name to ";
$copysheet_name = substr($copysheet_name,0,$MAXLENGTHSHEETNAME-3).'...';
print "$copysheet_name\n";
}
print "# adding '$file' sheet '$sheet->{'Name'}' ($copysheet_name)\n";
my $copysheet = $outbook->add_worksheet($copysheet_name);
$sheet->{'MaxRow'} ||= $sheet->{'MinRow'};
foreach my $row ($sheet->{'MinRow'} .. $sheet->{'MaxRow'})
{
my @rowdata = map { $sheet->{'Cells'}->[$row]->[$_]->{'Val'} }
$sheet->{'MinCol'} .. $sheet->{'MaxCol'};
$copysheet->write($row,0,\@rowdata);
}
}
}
elsif($file =~ /\.tab/)# .tab infiles, seria trivial manejar .csv por ejemplo
{
$copysheet_name = $filename;
print "# adding '$file' no_sheet ($copysheet_name)\n";
my $copysheet = $outbook->add_worksheet($copysheet_name);
open(INTAB,$file) || die "# $0 : cannot read '$file': $!";
while(<INTAB>)
{
chomp($_);
my @tabdata = split(/\t/,$_);
$copysheet->write($.-1,0,\@tabdata);
}
close(INTAB);
}
}
$outbook->close();
print "# outfile: $outfile\n";
Un saludo