【问题标题】:Perl reading huge excel filePerl读取巨大的excel文件
【发布时间】:2011-03-02 05:14:04
【问题描述】:

我有一个巨大的 xlsx 文件(大约 127 MB)并想使用 Spreadsheet::Excel 模块读取,但在 2GB RAM 机器上出现“内存不足”错误。 (注意该脚本适用于较小的 excel 2007 文件)

有没有什么方法可以逐行读取excel文件而不会达到内存限制?搜索谷歌我遇到了http://discuss.joelonsoftware.com/default.asp?joel.3.160328.14,但我不熟悉如何将电子表格存储到标量中。有人可以给我一个将 excel 2007 文件读取为标量和打印单元格值的示例。 下面是我在较小的电子表格上运行的当前脚本。

#!/usr/bin/perl
use Excel::Writer::XLSX;
use Spreadsheet::XLSX;
my $workbook  = Excel::Writer::XLSX->new('Book1.xlsx');
my $worksheet = $workbook->add_worksheet();
#  use strict;
my $excel = Spreadsheet::XLSX -> new ('Book2.xlsx');
my $date_format = $workbook->add_format();
$date_format->set_num_format('dd/mm/yy hh:mm');
# Columns of interest
@columns=(0,1,2,5,9,10,12,13,31);
@reportlist=("string1","String2","String3");
@actuallist=("ModifiedString1","ModifiedString2","ModifiedString3");
$max_list=$#reportlist;
foreach my $sheet (@{$excel -> {Worksheet}}) {
    printf("Sheet: %s\n", $sheet->{Name});
    $sheet -> {MaxRow} ||= $sheet -> {MinRow};
        foreach my $row ($sheet -> {MinRow} .. $sheet -> {MaxRow}) {
            $sheet -> {MaxCol} ||= $sheet -> {MinCol};
            for ($c=0;$c<=$#columns;$c++){
                $col=$columns[$c];
                my $cell = $sheet -> {Cells} [$row] [$col];
                    if($col==0){
                    $cell->{Val}=~ s/\ GMT\+11\:00//g;
                    $worksheet->write($row,$c,$cell->{Val},$date_format);
                    }
                    if ($cell) {
                        $worksheet->write($row,$c,$cell -> {Val});
                            for($z=0;$z<=$#reportisplist;$z++){
                                if(($cell->{Val})=~ m/$reportlist[$z]/i){
                                $worksheet->write($row,$c,$actuallist[$z]);
                                }
                            }
                    }
            }
        }
}
$workbook->close();

【问题讨论】:

    标签: perl excel spreadsheet


    【解决方案1】:

    我正在开发一个新模块,用于使用 Perl 快速、高效地读取 Excel xlsx 文件。它还没有在 CPAN 上(它需要更多的工作),但你可以在 GitHub 上获得它。

    这是一个如何使用它的示例:

    use strict;
    use warnings;
    use Excel::Reader::XLSX;
    
    my $reader   = Excel::Reader::XLSX->new();
    my $workbook = $reader->read_file( 'Book1.xlsx' );
    
    if ( !defined $workbook ) {
        die $reader->error(), "\n";
    }
    
    for my $worksheet ( $workbook->worksheets() ) {
    
        my $sheetname = $worksheet->name();
    
        print "Sheet = $sheetname\n";
    
        while ( my $row = $worksheet->next_row() ) {
    
            while ( my $cell = $row->next_cell() ) {
    
                my $row   = $cell->row();
                my $col   = $cell->col();
                my $value = $cell->value();
    
                print "  Cell ($row, $col) = $value\n";
            }
        }
    }
    
    __END__
    

    更新:此模块从未达到 CPAN 质量。请改用Spreadsheet::ParseXLSX

    【讨论】:

    • @Incognito:来自项目README这个项目实际上被放弃了
    【解决方案2】:

    您是否尝试过将 XLSX 转换为 csv 并将其作为纯文本文件读取?

    【讨论】:

    • 还没有。我还会遇到 CSV 内存不足的问题吗?
    • 由于它不会将整个文件读入内存并且几乎不使用任何数据结构,因此您不太可能出现内存不足错误。我已经处理了比这更大的 csv 文件,没有任何问题。
    • @Rasika 现在一切正常,将 xlsx 保存为 csv 并运行它要快得多。感谢您的提示
    【解决方案3】:

    试试这个。假设您已经安装了可以确定用于读取文件的实际解析器模块的 Spreadsheet::Read perl 模块,下面的代码片段读取并打印输入工作簿的第一个工作表的单元格。您可以检查 $workbook 对象以查看所有可配置的选项。该模块也可用于读取“csv”、“xls”等其他格式的文件。这是我发现有用的教程链接: http://search.cpan.org/~hmbrand/Spreadsheet-Read/Read.pm

    ReadData 可以通过传递选项来配置。它在每两个选项中都有很多选项,“cells”和“rc”可用于修改与文件读取相关的行为。默认情况下,这两个选项都设置为 true。如果“单元格”为真,则 ReadData 将工作簿的单元格存储在返回对象的哈希中。如果“rc”为真,则 ReadData 将工作簿的单元格存储在返回对象的数组中。在下面的代码片段中,通过设置 cells => 0 ,工作表的内容将不会以哈希格式存储在返回的 $workbook 对象中,从而节省了一些内存空间。默认情况下,此选项为 true,即 1 等。此外,为了进一步防止读取完整文件,您也可以将选项“rc”设置为 false。

    use Spreadsheet::Read;
    ############################################################################
    # function input  : file in xlsx format with absolute path 
    # function output : prints 1st worksheet content if exist
    ############################################################################
    sub print_xlsx_file{
    
        my $file_path = shift;
        my $workbook = ReadData($file_path,cells => 0 );
        if(defined $workbook->[0]{'error'}){
            print "Error occurred while processing $file_path:".
                  $workbook->[0]{'error'}."\n";
            exit(-1);
        }
        my $worksheet = $workbook->[1];
        my $max_rows = $worksheet->{'maxrow'};
        my $max_cols = $worksheet->{'maxcol'};
    
        for my $row_num (1..($max_rows))
        {
            for my $col_num (1..($max_cols)){
                print $worksheet->{'cell'}[$col_num][$row_num]."\n";
            }
        }
    }
    # call above function
    # print_xlsx_file("/home/chammu/mybook.xlsx");
    

    【讨论】:

      【解决方案4】:

      csv 解决方案是一个很好的解决方案。但也可以考虑另存为 xlsb - 它通常会提供类似的文件大小缩小,同时允许一些 excel 功能。 (会将此作为评论发布,但还没有声誉......)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-06
        • 1970-01-01
        • 2021-05-29
        相关资源
        最近更新 更多