Perl скрипт для преобразования xls в xlsx генерирует пустой xlsx и сообщение об ошибке - PullRequest
0 голосов
/ 27 мая 2020

выполняет приведенный ниже сценарий perl для преобразования xls в xlsx, но генерирует пустой xlsx, выдавая сообщение об ошибке ниже. Пожалуйста, помогите мне исправить код. Сообщение об ошибке:

Нечетное количество элементов в ha sh назначение в /usr/local/share/perl5/Spreadsheet/ParseExcel.pm строка 175.

#! /usr/bin/perl

use warnings;
use strict;

use Spreadsheet::ParseExcel;
use Excel::Writer::XLSX;

my $excel_xls = Spreadsheet::ParseExcel -> new ('Test.xls');
my ($sheet_xls, $row, $col, $cell);

my $excel_xlsx = Excel::Writer::XLSX->new('Test.xlsx');
my $sheet_xlsx = $excel_xlsx->add_worksheet();


for $sheet_xls ( @{ $excel_xls->{Worksheet} } ) {
    for $row ( $sheet_xls->{MinRow} .. $sheet_xls->{MaxRow} ) {
        for $col ( $sheet_xls->{MinCol} .. $sheet_xls->{MaxCol} ) {
            my $cell = $sheet_xls->{Cells}[$row][$col];
            print "$cell->{Val} ";
            $sheet_xlsx->write($row, $col, $cell->{Val});
        }
        print "\n";
    }
}

Ответы [ 3 ]

4 голосов
/ 28 мая 2020

Возможно, вам стоит внимательнее изучить документацию для метода new() в Spreadsheet :: ParseExcel . Примеры выглядят следующим образом:

my $parser = Spreadsheet::ParseExcel->new();

$parser = Spreadsheet::ParseExcel->new( Password => 'secret' );

$parser = Spreadsheet::ParseExcel->new(
    CellHandler => \&cell_handler,
    NotSetCell  => 1,
);

Метод не требует параметров или списка пар ключ / значение. Ваш вызов этого метода выглядит следующим образом:

my $excel_xls = Spreadsheet::ParseExcel -> new ('Test.xls');

Нет примера, который принимает имя файла в качестве параметра. Анализ файла - это двухэтапный процесс. Вы создаете объект парсера:

my $parser = Spreadsheet::ParseExcel->new();

А затем вы используете метод parse() для этого объекта для анализа электронной таблицы:

my $excel_xls = $parser->parse('Test.xls');

Если вы анализируете только один файл , то вы можете объединить эти две строки в одну:

my $excel_xls = Spreadsheet::ParseExcel->new()->parse('Test.xls');
0 голосов
/ 11 июня 2020

В соответствии с требованиями пользователя скрипт выполняет следующие операции.

  1. запись данных CSV в файл xlsx
  2. пользователю необходимо указать путь к входному каталогу (где собираются несколько файлов CSV для представления)
  3. пользователю необходимо указать выходной каталог (где будут создаваться файлы xlsx)
  4. скрипт выполняет следующую проверку
    • , если входной каталог не указан как команда строковый аргумент, показать ошибку
    • если выходной каталог не указан в качестве аргумента командной строки, показать ошибку
    • проверка наличия каталога
    • проверка файла csv, а также информации тега времени в имя файла
    • для создания файла xlsx обрабатывается только действительный файл csv.

Код

use strict;
use warnings;

use Getopt::Long;
# this module helps to read csv data from file
use Text::CSV_XS qw( csv );
# this module helps to create xlsx file
use Excel::Writer::XLSX;
use File::Basename;

# get files from input directory
sub get_files {
    my $dir = shift // '.';
    my @all_files = ();
    opendir my $dh, $dir or die "Could not open '$dir' for reading '$!'\n";
    my @files = readdir $dh;
    foreach my $file (@files) {
        if ($file eq '.' or $file eq '..') {
            next;
        }
        # check for file .csv
        if ((( split /\./, $file )[-1]) eq "csv") {
            # winodws os syntax
            push @all_files, "$dir\\$file";
            # linux os syntax
            #push @all_files, "$dir/$file";
        }
    }
    closedir $dh;
    return @all_files;
}

# get digit from string
sub get_digit {
    my ($string) = @_;
    my ($digit) = $string =~ /(\d+)/;
    return ((defined $digit) ? $digit : '');
}

# write data to excel file
sub write_excel {
    my ($worksheet,$aoa) = @_;
    my $row = 0;
    foreach my $internal_array_ref (@$aoa) {
        my $col=0;
        foreach my $element (@$internal_array_ref) {
            # if element is not having any value
            $element = '' unless defined $element;
            $worksheet->write_string( $row, $col++, $element );
        }
        $row++;
    }
}

### Main ##
my $csv_dir;
my $output_dir;
# command line arguments
# input directory (--idir)which going to contain csv files
# ouput directory (--odir) where xlsx files going to be created
GetOptions(
    'idir=s' => \$csv_dir,
    'odir=s' => \$output_dir,
) or die "Usage: $0 --idir Directory PATH -odir DIRECTORY PATH \n";

# if input directory is not specified  as an input argument, show error
if (not defined $csv_dir) {
    print "\n Argument 'idir' is mandatory \n";
    print "for example $0 --ifile DIRPATH";
    exit 1;
}

# if output directory is not specified as an input argument, show error
if (not defined $output_dir) {
    print "\n Argument 'odir' is mandatory \n";
    print "for example $0 --odir DIRECTORY PATH";
    exit 1;
}

# check for input and output directory path
if ((-d $csv_dir) && (-d $output_dir)) {
    # get all csv files from input directory
    my @csv_files = get_files($csv_dir);
    # read csv file data and create xlsx file
    foreach my $csv_file (@csv_files) {
        # Check for file contain date digit
        my $date_digit = get_digit($csv_file);
        if ($date_digit eq '') {
            print "\n $csv_file not contain date information \n";
        } else {
            # excel file name with date digit
            # get file name from given file path (except extension)
            my $filename = basename("$csv_file",  ".csv");
            # this syntax is for windows to create xlsx file path
            my $excel_file = "$output_dir\\$filename.xlsx";
            # this syntax is for windows to create xlsx file path
            #my $excel_file = "$output_dir/$filename.xlsx";

            #  Read whole file in memory (as array of array)
            # change seperate char as per your csv file data
            # change quote char as per your csv file data (else just mentioned undef)
            # change escape char as per your csv file data (else just mentioned undef)
            eval {
                my $aoa = csv (in => $csv_file,     
                        encoding => "UTF-8",
                        sep_char    => ',',
                        quote_char  => '"',
                        escape_char => undef);

                if (scalar @$aoa) {
                    # Create a new Excel workbook
                    my $workbook = Excel::Writer::XLSX->new( $excel_file );
                    # Add a worksheet
                    my $worksheet = $workbook->add_worksheet();
                    # write csv data to excel file
                    write_excel($worksheet,$aoa);
                    $workbook->close();
                    print "\n The $excel_file created sucessfully. \n";
                }
            };

            if ($@) {
                print "\n Invalid CSV file or check CSV file format \n";
            }
        }
    }
}  else {
    print "\n Please provide valid directiory path: $csv_dir or Please provide valid directory path $output_dir";
    exit 1;
}

Синтаксис вывода и выполнения

### Windows
C:\Users\batman\source\repos>perl csv_xlsx.pl --idir C:\Users\batman\source\repos\csv --odir C:\Users\batman\source\repos\excel_out

 The C:\Users\batman\source\repos\excel_out\sample_dept1_20200609.xlsx created sucessfully.

 The C:\Users\batman\source\repos\excel_out\sample_dept2_20200610.xlsx created sucessfully.

### Linux
perl csv_xlsx.pl --idir /Users/batman/source/repos/csv --odir /Users/batman/source/repos/excel_out
0 голосов
/ 11 июня 2020

В соответствии с вашим новым требованием (передать путь к файлу параметров программе) я добавил необходимый код. Установить (Getopt :: Long perl модуль)

use strict;
use warnings;

use Getopt::Long;
use Text::CSV_XS qw( csv );
use Excel::Writer::XLSX;

# get digit from string
sub get_digit {
    my ($string) = @_;
    my ($digit) = $string =~ /(\d+)/;
    return ((defined $digit) ? $digit : '');
}

# write data to excel file
sub write_excel {
    my ($worksheet,$aoa) = @_;
    my $row = 0;
    foreach my $internal_array_ref (@$aoa) {
        my $col=0;
        foreach my $element (@$internal_array_ref) {
            # if element is not having any value
            $element = '' unless defined $element;
            $worksheet->write( $row, $col++, $element );
        }
        $row++;
    }
}

### Main ##
my $csv_file;
my $excel_file;
GetOptions('file=s' => \$csv_file) or die "Usage: $0 --file FILENAME\n";
if (not defined $csv_file) {
    print "\n Argument 'file' is mandatory \n";
    print "for example $0 --file FILEPATH";
    exit 1;
}
# check for file exists and not empty
if (-s $csv_file) {
    # Check for file contain date digit
    my $date_digit = get_digit($csv_file);
    if ($date_digit eq '') {
        print "\n $csv_file not contain date information \n";
        exit 1;
    } else {
        # excel file name with date digit
        $excel_file = "csv_to_excel_$date_digit.xlsx";
        #  Read whole file in memory (as array of array)
        # change seperate char as per your csv file data
        # change quote char as per your csv file data (else just mentioned undef)
        # change escape char as per your csv file data (else just mentioned undef)
        eval {
            my $aoa = csv (in => $csv_file,     
                        encoding => "UTF-8",
                        sep_char    => ',',
                        quote_char  => '"',
                        escape_char => undef);

            if (scalar @$aoa) {
                # Create a new Excel workbook
                my $workbook = Excel::Writer::XLSX->new( $excel_file );
                # Add a worksheet
                my $worksheet = $workbook->add_worksheet();
                # write to excel file
                write_excel($worksheet,$aoa);
                $workbook->close();
                print "\n The $excel_file created sucessfully. \n";
            }
        };

        if ($@) {
            print "\n Invalid CSV file or check CSV file format \n";
            exit 1;
        }
    }
}  else {
    print "\n Please provide valid file path: $csv_file";
    exit 1;
}

вывод

perl csv_xlsx.pl

 Argument 'file' is mandatory
for example csv_xlsx.pl --file FILEPATH
##############
perl csv_xlsx.pl --file
Option file requires an argument
Usage: csv_xlsx.pl --file FILENAME
#############
perl csv_xlsx.pl --file sample.csv

 sample.csv not contain date information
###############
perl csv_xlsx.pl --file sample_20200609.csv

 The csv_to_excel_20200609.xlsx created sucessfully
...