将 xls 转换为 xlsx 的 Perl 脚本生成空白 xlsx 和错误消息

Perl script to convert xls to xlsx is generating blank xlsx and error message

正在执行以下 perl 脚本将 xls 转换为 xlsx,但它会通过给出以下错误消息生成空白 xlsx。请帮助我更正代码。错误信息:

Odd number of elements in hash assignment at /usr/local/share/perl5/Spreadsheet/ParseExcel.pm line 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";
    }
}

也许你应该稍微仔细看看documentation for the new() method in Spreadsheet::ParseExcel。示例如下所示:

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

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

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

该方法不需要任何参数或 key/value 对列表。您对该方法的调用如下所示:

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');

根据您的新要求(将参数文件路径传递给程序),我添加了必要的代码。安装(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: [=10=] --file FILENAME\n";
if (not defined $csv_file) {
    print "\n Argument 'file' is mandatory \n";
    print "for example [=10=] --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;
}

output

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

根据用户要求,脚本正在执行以下操作。

  1. 将 CSV 数据写入 xlsx 文件
  2. 用户需要提供输入目录路径(其中将出现多个 csv 文件)
  3. 用户需要提供输出目录(将在其中创建 xlsx 文件)
  4. 脚本正在执行以下验证
    • 如果输入目录未指定为命令行参数,则显示错误
    • 如果未将输出目录指定为命令行参数,则显示错误
    • 正在检查目录是否存在
    • 正在检查 csv 文件以及文件名中的时间标签信息
    • 仅处理有效的 csv 文件以创建 xlsx 文件。

Code

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: [=10=] --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 [=10=] --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 [=10=] --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;
}

Output and execution syntax

### 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