2013-08-31 2 views
0

Я хочу конвертировать excel-файлы в csv-файлы с помощью Perl. Для удобства я хотел бы использовать модуль File :: Slurp для операций чтения/записи. Мне нужно это в подфункции.Perl - пустые строки при записи CSV из Excel

При печати на экране программа генерирует желаемый результат, созданные 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, но никак не удавалось зафиксировать его.

Есть идеи?

спасибо, что заранее.

+1

Добавить 'use strict; использовать предупреждения; 'и сообщать об ошибках/предупреждениях, которые вы получаете. Я думаю, что 'use v5.14' активирует строгие, но не предупреждения ... но используйте их обоих, чтобы быть уверенными. – TLP

+0

Вам следует знать, что вы рискуете перезаписать исходные файлы с помощью строки '$ outFile = ~ s/xls/csv/g'. В Windows '<*.xls>' будет соответствовать чему-то вроде 'foo.XLS', но ваше регулярное выражение чувствительно к регистру и не будет выполнять подстановку, поэтому ваши имена входов и выходов будут одинаковыми. Используйте '/ i', чтобы игнорировать регистр. – TLP

+0

Спасибо за подсказку с чувствительным к регистру. Я добавил использование строго; использовать предупреждения; Единственное предупреждение, которое я получаю, это «Широкий символ в печати на линии etl.pl 45». Но это потому, что в моих файлах есть такие символы, как «ö». – royskatt

ответ

1

write_file будет перезаписывать файл, если не указан параметр append => 1. Таким образом:

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)', поскольку это может означать, что пустые ячейки пропускаются. Вы можете использовать определенный или оператор, например 'my $ value = $ source_cell-> value // "" ' – TLP

0

Ну, в данном конкретном случае File :: Slurp действительно усложнял это для меня. Я просто хотел, чтобы избежать повторять себя, что я и сделал в следующем неуклюжем рабочем растворе:

#!/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(); 

Я на самом деле не счастлив с ней, так как я открываю и так часто закрывать файлы (у меня есть много файлы со многими строками). Это не очень умно с точки зрения производительности.

В настоящее время я до сих пор не знаю, как использовать split или Text: CSV в этом случае, чтобы помещать все в массив и открывать, записывать и закрывать каждый файл только один раз.

Спасибо за ваш ответ TLP.

+0

Вам не нужно открывать для добавления ('>>') один раз для каждого значения. Просто используйте режим '>' один раз для каждого файла. – TLP

Смежные вопросы