NAME

Excel::ValueReader::XLSX - extracting values from Excel workbooks in XLSX format, fast

SYNOPSIS

my $reader = Excel::ValueReader::XLSX->new(xlsx => $filename);
# .. or with syntactic sugar :
my $reader = Excel::ValueReader::XLSX->new($filename);
# .. or with LibXML backend :
my $reader = Excel::ValueReader::XLSX->new(xlsx => $filename,
                                           using => 'LibXML');

foreach my $sheet_name ($reader->sheet_names) {
   my $grid = $reader->values($sheet_name);
   my $n_rows = @$grid;
   print "sheet $sheet_name has $n_rows rows; ",
         "first cell contains : ", $grid->[0][0];
}

DESCRIPTION

This module reads the contents of an Excel file in XLSX format; given a worksheet name it returns a bidimensional array of values in that worksheet.

Unlike Spreadsheet::ParseXLSX or Spreadsheet::XLSX, there is no support for reading formulas, formats or other Excel internal information; all you get are plain values -- but you get them much faster !

This front module has two different backends for extracting values :

Regex (default)

this backend uses regular expressions to parse the XML content.

LibXML

this backend uses XML::LibXML::Reader to parse the XML content. It is probably safer but about three times slower than the Regex backend (but still much faster than Spreadsheet::ParseXLSX).

METHODS

new

my $reader = Excel::ValueReader::XLSX->new(xlsx  => $filename,
                                           using => $backend);

The xlsx argument is mandatory and points to the .xlsx file to be parsed. The using argument is optional; it specifies the backend to be used for parsing; default is 'Regex'.

As syntactic sugar, a shorter form is admitted :

my $reader = Excel::ValueReader::XLSX->new($filename);

sheet_names

my @sheets = $reader->sheet_names;

Returns the list of worksheet names, in the same order as in the Excel file.

values

my $grid = $reader->values($sheet);

Returns a bidimensional array of scalars, corresponding to cell values in the specified worksheet. The $sheet argument can be either a sheet name or a sheet position (starting at 1).

Unlike the original Excel cells, positions in the grid are zero-based, so for example the content of cell B3 is in $grid->[1][2]. The grid is sparse : the size of each row depends on the position of the last non-empty cell in that row. Thanks to Perl's auto-vivification mechanism, any attempt to access a non-existent cell will automatically create the corresponding cell within the grid. The number of rows and columns in the grid can be computed like this :

my $nb_rows = @$grid;
my $nb_cols = max map {scalar @$_} @$grid; # must import List::Util::max

CAVEATS

  • This module was optimized for speed, not for completeness of OOXML-SpreadsheetML support; so there may be some edge cases where the output is incorrect with respect to the original Excel data.

  • Excel dates are stored internally as numbers, so they will appear as numbers in the output. To convert numbers to dates, use the DateTime::Format::Excel module. Unfortunately the module has currently no support for identifying which cells contain dates; this would require to parse cell formats -- maybe this will be implemented in a future release.

  • Embedded newline characters in strings are stored in Excel as \r\n, following the old Windows convention. When retrieved through the Regex backend, the result contains the original \r and \n characters; but when retrieved through the LibXML, \r are silently removed by the XML::LibXML package.

SEE ALSO

The official reference for OOXML-SpreadsheetML format is in https://www.ecma-international.org/publications/standards/Ecma-376.htm.

Introductory material on XLSX file structure can be found at http://officeopenxml.com/anatomyofOOXML-xlsx.php.

The CPAN module Data::XLSX::Parser is claimed to be in alpha stage; it seems to be working but the documentation is insufficient -- I had to inspect the test suite to understand how to use it.

Another unpublished but working module for parsing Excel files in Perl can be found at https://github.com/jmcnamara/excel-reader-xlsx. Some test cases were borrowed from that distribution.

BENCHMARKS

Below are some benchmarks computed with the program benchmark.pl in this distribution. The task was to parse an Excel file of five worksheets with about 62600 rows in total, and report the number of rows per sheet. Reported figures are in seconds.

Excel::ValueReader::XLSX::Regex    11 elapsed,  10 cpu, 0 system
Excel::ValueReader::XLSX::LibXML   35 elapsed,  34 cpu, 0 system
[unpublished] Excel::Reader::XLSX  39 elapsed,  37 cpu, 0 system
Spreadsheet::ParseXLSX            244 elapsed, 240 cpu, 1 system
Data::XLSX::Parser                 37 elapsed,  35 cpu, 0 system

These figures show that the regex version is about 3 times faster than the LibXML version, and about 22 times faster than Spreadsheet::ParseXLSX. Tests with a bigger file of about 90000 rows showed similar ratios.

Modules Excel::Reader::XLSX (unpublished) and Data::XLSX::Parser are based on XML::LibXML like Excel::ValueReader::XLSX::LibXML; execution times for those three modules are very close.

ACKNOWLEDGEMENTS

  • David Flink signaled (and fixed) a bug about strings with embedded newline characters

AUTHOR

Laurent Dami, <dami at cpan.org>

COPYRIGHT AND LICENSE

Copyright 2020 by Laurent Dami.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.