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 theRegex
backend, the result contains the original\r
and\n
characters; but when retrieved through the LibXML,\r
are silently removed by theXML::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.