从使用Perl创build的Excel 2007文件中读取标题行时出现问题

我有一个合并两个dynamic创build的Excel 2007文件的问题。 我的文件是在Solaris上用Perl模块Excel :: Writer :: XLSX创build的。

说我有两个文件,fileA.xlsx和fileB.xlsx。 现在我想合并它们(fileA + fileB => fileC)。 目前不可能 fileB 附加到fileA。 这是Excel :: Writer :: XLSX,它只能创build新的文件的限制。

即使在Excel 2003中,这两个.xlsx文件都可以在Excel 2007,LibreOffice 3(在Linux上)以及(在Microsoft的xlsx到xls转换器的帮助下)下打开。

但是,当我用Perl打开它们(使用模块Spreadsheet :: XLSX )时,标题行(第0行)的内容总是被跳过;

# ... foreach my $infile (@infiles) { my $excel = Spreadsheet::XLSX->new($infile); my $i = 0; foreach my $sheet ( @{ $excel->{Worksheet} } ) { printf( "Infile '$infile', Sheet $i: %s\n", $sheet->{Name} ); $sheet->{MaxRow} ||= $sheet->{MinRow}; print "$infile: " . $sheet->{MaxRow} . " rows\n"; print "data starts at row: " . $sheet->{MinRow} . ". \n"; next unless $i == 0; # only copy data from the first sheet (for speed) my $start_row = $sheet->{MinRow}; foreach my $row ( $start_row .. $sheet->{MaxRow} ) { $sheet->{MaxCol} ||= $sheet->{MinCol}; foreach my $col ( $sheet->{MinCol} .. $sheet->{MaxCol} ) { my $cell = $sheet->{Cells}[$row][$col]; if ($cell) { # do something with the data # ... # write to outfile $excel_writer->sheets(0)->write($dest_row, $col, $cell->{Val} ) } } } } } 

现在,这个代码片段的输出总是

 data starts at row: 1. 

但是这不是真的,它开始于第0行。如果我手动去从row0读取数据,$ cell是未定义的(尽pipe它不应该是)。

有趣的是,当我在Microsoft Excel中打开该文件并对其进行简单的更改(例如,通过为标题行中的一个单元格值添加空格)并保存该文件,则代码find标题行以上。

 data starts at row: 0. 

顺便说一下,当我打开,更改,保存LibreOffice中的文件时,有许多有关date值的警告,当我重新阅读上面的代码。 (因此,date时间值似乎被LibreOffice稍微保存不正确)。

生成这些文件的代码如下所示(注意:一些variables是在这个子文件之外定义的):

 sub exportAsXLS { #require Spreadsheet::WriteExcel; require Excel::Writer::XLSX; my ( $data, $dir, $sep, @not2export ) = @_; my $val; my $EXCEL_MAXROW = 1048576; return undef unless $data; return "." unless scalar @$data > 0; my $time = time2str( "%Y%m%d_%H%M%S", time() ); my $file = "$outdir/$dir/${host}_${port}-${time}.xlsx"; #my $workbook = Spreadsheet::WriteExcel->new($file); my $workbook = Excel::Writer::XLSX->new($file); $workbook->set_optimization(); my $worksheet = $workbook->add_worksheet(); # Set the default format for dates. #my $date_formatHMS = $workbook->add_format( num_format => 'mmm d yyyy hh:mm AM/PM' ); #my $date_formatHMS = $workbook->add_format( num_format => 'yyyy-mm-ddThh:mm:ss.sss' ); my %formats; $formats{date_HM} = $workbook->add_format( num_format => 'yyyy-mm-ddThh:mm' ); $formats{date_HMS} = $workbook->add_format( num_format => 'yyyy-mm-ddThh:mm:ss' ); $formats{num} = $workbook->add_format(); $formats{num}->set_num_format(); $formats{headline} = $workbook->add_format(); $formats{headline}->set_bold(); $formats{headline}->set_num_format('@'); # Format as a string. use the Excel text format @: # Doesn't change to a number when edited $formats{string} = $workbook->add_format( num_format => '@' ); $worksheet->set_row( 0, 15, $formats{headline} ); my $row = 0; my $col = 0; for ( my $r = -1 ; $r < @$data && $r < $EXCEL_MAXROW ; $r++ ) { for ( my $i = 0 ; $i < @$column ; $i++ ) { next if grep( $_ eq $column->[$i], @not2export ); my $val = $data->[$r]{ $column->[$i] }; my $t = int $type->[$i]; if ( $r < 0 ) { #warn " type: $type->[$i] , "; # Erste Zeile = Spaltennamen ausgeben $worksheet->write_string( $row, $col++, $column->[$i], $formats{string}); #$worksheet->write_comment( 0, 0, "\x{263a}" ); # Smiley #$worksheet->write( $row, $col++, $column->[$i], $formats{headline} ); } elsif ( ( $t == 11 ) or ( $t == 9 ) ) { # 11 - Der Wert ist ein Datum, im SHORT Format, 9- long $val = time2str( "%Y-%m-%dT%H:%M:%S", str2time( $data->[$r]{ $column->[$i] } ) ); $worksheet->write_date_time( $row, $col++, $val, $formats{date_HMS} ); } else { $worksheet->write( $row, $col++, $val ); } } $col = 0; $row++; } return $file; 

}

这些文件之间的区别如下。 xml文件差异

左边是Excel :: Writer :: XLSX生成的文件。 在右边是MS Excel 2003在对标题行进行微小更改之后生成的文件。 行头数据被重构,外部化到不同的文件sharedStrings.xml

看起来像这样

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="5" uniqueCount="5"> <si> <t>SITE</t> </si> <si> <t>LOG_DATE</t> </si> <si> <t>KTZI201_WF_TEMPERATUR</t> </si> <si> <t>KTZI300_TEMP_RESERVOIR</t> </si> <si> <t>XPEDITION</t> </si> </sst> 

如果.xlsx文件格式化为如图右半部分所示,Spreadsheet :: XLSX可以读取头文件,但如果格式化为左半部分,则跳过标题行。

当我对此Excel :: Writer :: XLSX 示例程序的输出运行您的程序时,它正确报告第一行(行== 0)中的数据:

 Infile 'a_simple.xlsx', Sheet 0: Sheet1 a_simple.xlsx: 10 rows data starts at row: 0. 

也许你应该仔细检查产生input文件的程序。

另外,请确保您使用的是最新版本的Excel :: Writer :: XLSX 。