2013-08-31 36 views
0

我想用Perl將excel文件轉換爲csv文件。爲了方便,我喜歡使用模塊File :: Slurp進行讀/寫操作。我需要它在一個子功能。Perl - 從Excel寫入CSV時的空行

雖然打印出到屏幕上,該程序生成期望的輸出,生成CSV-文件不幸只是包含一排以分號,字段是空的。

下面是代碼:

#!/usr/bin/perl 

use File::Copy; 
use v5.14; 
use Cwd; 
use File::Slurp; 
use Spreadsheet::ParseExcel; 


sub xls2csv { 
    my $currentPath = getcwd(); 
    my @files  = <$currentPath/stage0/*.xls>; 

    for my $sourcename (@files) { 
     print "Now working on $sourcename\n"; 
     my $outFile = $sourcename; 
     $outFile =~ s/xls/csv/g; 
     print "Output CSV-File: ".$outFile."\n"; 
     my $source_excel = new Spreadsheet::ParseExcel; 
     my $source_book = $source_excel->Parse($sourcename) 
      or die "Could not open source Excel file $sourcename: $!"; 

     foreach my $source_sheet_number (0 .. $source_book->{SheetCount} - 1) 
     { 
      my $source_sheet = $source_book->{Worksheet}[$source_sheet_number]; 

      next unless defined $source_sheet->{MaxRow}; 
      next unless $source_sheet->{MinRow} <= $source_sheet->{MaxRow}; 
      next unless defined $source_sheet->{MaxCol}; 
      next unless $source_sheet->{MinCol} <= $source_sheet->{MaxCol}; 

      foreach my $row_index (
       $source_sheet->{MinRow} .. $source_sheet->{MaxRow}) 
      { 
       foreach my $col_index (
        $source_sheet->{MinCol} .. $source_sheet->{MaxCol}) 
       { 
        my $source_cell = 
         $source_sheet->{Cells}[$row_index][$col_index]; 
        if ($source_cell) { 

         print $source_cell->Value, ";"; # correct output! 

         write_file($outFile, { binmode => ':utf8' }, $source_cell->Value, ";"); # only one row of semicolons with empty fields! 
        } 
       } 
       print "\n"; 
      } 
     } 

    } 
} 

xls2csv(); 

我知道它是與傳入WRITE_FILE功能參數,但不能設法解決它。

有沒有人有想法?

非常感謝您提前。除非append => 1給出選項

+1

加上'use strict;使用警告;'並報告你得到的錯誤/警告。我認爲'使用v5.14'激活嚴格,但不警告..但使用它們都是確定的。 – TLP

+0

您應該知道,您可能會冒險用'$ outFile =〜s/xls/csv/g'這行來覆蓋原始文件。在Windows中,'<*.xls>'會匹配類似'foo.XLS'的東西,但是你的正則表達式區分大小寫,並且不會執行替換,所以你的輸入和輸出文件名是相同的。使用'/ i'來忽略大小寫。 – TLP

+0

感謝提示區分大小寫。我加了使用嚴格;使用警告; 我得到的唯一警告是「寬字符打印在etl.pl行45.」。但那是因爲我的文件中有「ö」等字符。 – royskatt

回答

1

write_file將覆蓋該文件。所以這個:

write_file($outFile, { binmode => ':utf8' }, $source_cell->Value, ";"); 

會爲每個新的單元值寫一個新的文件。然而,它不符合「只有一行空分欄的分號」的描述,因爲它只應該是一個分號和一個值。

我對這份感情值得懷疑從您:"For convenience I like to use the module File::Slurp"。雖然print聲明按原樣工作,但使用File::Slurp則不會。那麼這很方便嗎?

如果您還想使用write_file,您應該做的是收集所有要打印的行,然後在循環結束時立即將它們全部打印出來。例如: -

$line .= $source_cell->Value . ";"; # use concatenation to build the line 
... 
push @out, "$line\n";     # store in array 
... 
write_file(...., \@out);    # print the array 

另一個簡單的辦法是使用join,或使用Text::CSV模塊。

+0

正如我在下面提到的那樣,第一個問題解決了,雖然不是很優雅。 不幸的是另一個彈出:當我有Excel文件中的空白列時,CSV文件中沒有相應的字段生成(缺少分號)。 :-( – royskatt

+0

上面提到的另一個問題是:爲什麼在write_file中使用引用:\ @out而不僅僅是@out?有什麼不同? – royskatt

+0

@royskatt當我瀏覽文檔時,發現它是(可能的)一個小的優化,因爲你不必複製數據,當你傳遞一個數組時,數組會被展開並且元素被複制到'@ _'。至於空白字段......你必須確保即使單元格爲空,也會打印一個值,例如,檢查'if($ source_cell)'可能是不正確的,因爲它可能意味着空白單元格被忽略,您可以使用defined或operator,例如'my $ value = $ source_cell-> value //「」' – TLP

0

那麼,在這種特殊情況下,文件::嘟嘟地喝確實複雜化這對我來說。我只是想避免重複自己,這是我在下面的笨拙工作的解決方案所做的:

#!/usr/bin/perl 

use warnings; 
use strict; 
use File::Copy; 
use v5.14; 
use Cwd; 
use File::Basename; 
use File::Slurp; 
use Tie::File; 
use Spreadsheet::ParseExcel; 
use open qw/:std :utf8/; 

# ... other functions 

sub xls2csv { 
    my $currentPath = getcwd(); 
    my @files  = <$currentPath/stage0/*.xls>; 
    my $fh; 

    for my $sourcename (@files) { 
     say "Now working on $sourcename"; 
     my $outFile = $sourcename; 
     $outFile =~ s/xls/csv/gi; 
     if (-e $outFile) { 
      unlink($outFile) or die "Error: $!"; 
      print "Old $outFile deleted."; 
     } 
     my $source_excel = new Spreadsheet::ParseExcel; 
     my $source_book = $source_excel->Parse($sourcename) 
      or die "Could not open source Excel file $sourcename: $!"; 

     foreach my $source_sheet_number (0 .. $source_book->{SheetCount} - 1) 
     { 
      my $source_sheet = $source_book->{Worksheet}[$source_sheet_number]; 

      next unless defined $source_sheet->{MaxRow}; 
      next unless $source_sheet->{MinRow} <= $source_sheet->{MaxRow}; 
      next unless defined $source_sheet->{MaxCol}; 
      next unless $source_sheet->{MinCol} <= $source_sheet->{MaxCol}; 

      foreach my $row_index (
       $source_sheet->{MinRow} .. $source_sheet->{MaxRow}) 
      { 
       foreach my $col_index (
        $source_sheet->{MinCol} .. $source_sheet->{MaxCol}) 
       { 
        my $source_cell = 
         $source_sheet->{Cells}[$row_index][$col_index]; 
        if ($source_cell) { 
         print $source_cell->Value, ";"; 
         open($fh, '>>', $outFile) or die "Error: $!"; 
         print $fh $source_cell->Value, ";"; 
         close $fh; 
        } 
       } 
       print "\n"; 
       open($fh, '>>', $outFile) or die "Error: $!"; 
       print $fh "\n"; 
       close $fh; 
      } 
     } 

    } 
} 

xls2csv(); 

實際上,我不滿意的話,因爲我開等常閉文件(我有很多多行文件)。在性能方面這不是很聰明。

目前我還是不知道如何使用拆分或文字:CSV在這種情況下,爲了把一切到一個數組,打開,寫入和關閉每個文件只一次。

謝謝你的回答TLP。

+0

對於每個值,您不必打開追加('>>')一次。每個文件只需使用一次'>'模式。 – TLP