ravi45722 has asked for the wisdom of the Perl Monks concerning the following question:

I am opening .xls template (template.xls) using

use Spreadsheet::ParseExcel; use Spreadsheet::ParseExcel::SaveParser;

after writing it my data into that i am saving it with

 SaveAs('updated.xls');

Like this i have a .xlsx template file. I need to open it without loosing previous data and write my data into it every day and close it in .xlsx format. For that I tried like this

#!/usr/bin/perl # # use strict; use warnings; use Spreadsheet::ParseXLSX; use Spreadsheet::ParseExcel; use Spreadsheet::ParseExcel::SaveParser; use Spreadsheet::ParseExcel::Workbook; use DBI; use POSIX qw(strftime); my $database = "ravi"; my $host = "XXX.XX.X.XXX"; my $username = "root"; my $table = "CDR"; my $dbh = DBI->connect("DBI:mysql:database=$database;host=$host;mysql_ +socket=/opt/lampstack-5.5.27-0/mysql/tmp/mysql.sock","root","", +{'RaiseError' => 1}); my $date=$ARGV[0]; $date or $date=`date --date='1 day ago' +%Y%m%d`; chomp $date; my $parser = Spreadsheet::ParseXLSX->new; my $template = $parser->parse("CDR.xlsx"); my $sth = $dbh->prepare("SELECT * from $table where Date='$date'"); $sth->execute() or die $DBI::errstr; my $sheet = $template->worksheet(2); my $row = 1; while (my @row = $sth->fetchrow_array()) { my $col=1; foreach my $value (@row) { $sheet->AddCell( $row, $col, $value ); ++$col; } $row++; } $template->SaveAs("CDR.xlsx"); print "Success";

But its showing an error

 Can't locate object method "SaveAs" via package "Spreadsheet::ParseExcel::Workbook" at ./parse_xlsx.pl line 43.

i am using linux. Cant use Win::OLE32

Thanks for any information

Replies are listed 'Best First'.
Re: Modules for xlsx files
by tangent (Parson) on Sep 28, 2015 at 13:01 UTC
    John MacNamara, who knows a thing or two about Excel files, states:
    An Excel file is a binary file within a binary file. It contains several interlinked checksums and changing even one byte can cause it to become corrupted. As such you cannot simply append or update an Excel file. The only way to achieve this is to read the entire file into memory, make the required changes or additions and then write the file out again.
    The docs for Spreadsheet::ParseExcel::SaveParser state that the module works "by reading it with Spreadsheet::ParseExcel and rewriting it with Spreadsheet::WriteExcel". For .xlsx files you will have to do that bit yourself.

    To read in use: Spreadsheet::ParseXLSX

    To write out use: Excel::Writer::XLSX

    There is also a module called Excel::Template::XLSX which may be of interest.
Re: Modules for xlsx files
by u65 (Chaplain) on Sep 28, 2015 at 11:17 UTC

    First of all, I don't see where you are using the "ParseExcel" modules in your code. Second, as the error message says and the CPAN documentation shows, module Spreadsheet::ParseXLSX has no "SaveAs" function in spite of what the module author says about it being an adaptor of module Spreadsheet::ParseExcel.

    Note the modules above are parsers, not writers. I recommend you try module Spreadsheet::WriteExcelXML in your code.

      Ya I am using

      use Spreadsheet::ParseExcel; use Spreadsheet::ParseExcel::SaveParser; use Spreadsheet::ParseExcel::Workbook;
      because with when I start reading I saw that Spreadsheet::ParseXLSX an adaptor from Spreadsheet::ParseExcel . When I got error I think its necessary and included it. Note the modules above are parsers, not writers Then How its working here.

      Here is the program which taking the data from DB and opening template(.xls) and writing the DB data into it using Spreadsheet::ParseExcel

      #!/usr/bin/perl use DBI; use Spreadsheet::ParseExcel; use Spreadsheet::ParseExcel::SaveParser; #use Spreadsheet::Read; $date=$ARGV[0];#yyyy-mm-dd $month=$ARGV[1];#August $date or $date=`date --date='1 day ago' +%Y-%m-%d`; $month or $month=`date --date='1 day ago' +%B`; chomp $month; chomp $date; print "Month:$month\n"; $Database_name="Busy_Hour_Report"; $IP_address="xxx.xx.xx.xxx"; $command = "cp /root/prac/packages/B_Mum_Busy_hour_report_fdb.xls /roo +t/prac/packages/B_Mum_Busy_hour_report_fdb_$date.xls"; print $command; `$command`; my $dbh = DBI->connect("DBI:mysql:database=$Database_name;host=$IP_add +ress;mysql_socket=/opt/lampstack-5.5.27-0/mysql/tmp/mysql.sock","root +","", {'RaiseError' => 1}); #--------------------------------------------------------------------- +------------------------------------# #--------------------------------------------------------------------- +------------------------------------# sub parser_excel { my $sql_comm=$_[0]; my $sheet_no=$_[1]; #Selecting the data to fetch my $sth = $dbh->prepare("$sql_comm"); $sth->execute() or die $DBI::errstr; # Open an existing file with SaveParser my $parser = Spreadsheet::ParseExcel::SaveParser->new(); my $template = $parser->Parse("B_Mum_Busy_hour_report_fdb_$dat +e.xls"); # Get the first worksheet. my $sheet = $template->worksheet($sheet_no); my $row = 5; my $col = 1; while (my @row = $sth->fetchrow_array()) { foreach my $value (@row) { $sheet->AddCell( $row, $col, $value ); ++$col; } $row++; $col=1; } $template->SaveAs("B_Mum_Busy_hour_report_fdb_$date.xls"); } parser_excel("Select * from Busy_Hour where monthname(Date)='$month' O +RDER BY Date DESC",0); parser_excel("Select * from Hourly_Data where monthname(Date)='$month' + and Hour in (21,22,23) ORDER BY Date DESC",1); parser_excel("Select * from Hourly_Data where Date='$date' order by Ho +ur",2);
        You are not using Spreadsheet::ParseExcel to write - Spreadsheet::ParseExcel::SaveParser uses two different modules for read/write:
        my $template = $parser->Parse("B_Mum_Busy_hour_report_fdb_$date.xls");
        reads entire Excel file into memory using Spreadsheet::ParseExcel
        $template->SaveAs("B_Mum_Busy_hour_report_fdb_$date.xls");
        overwrites old Excel file using Spreadsheet::WriteExcel

      Please read the questions carefully. I clearly mention that i need to open a template and write data into that. i dont need to open a new .xlsx file. With Spreadsheet::WriteExcelXML is it possible to open a existing file???

        First, I see the author has made a new module Excel::Writer::XLSX to replace the deprecated Spreadsheet::WriteExcelXML.

        I would use separate reading and writing processes. First save the current file as a new, temp name. Read it to get data into suitable form. Write the new file updated as desired. Save the new, updated file. The clear separation should help maintenance of your code.

        I have used Excel readers and writers a lot but am not aware of any that can read and write the same stream, but then I have never needed an update capability either.

        i need to open a template and write data into that. i dont need to open a new .xlsx file. With Spreadsheet::WriteExcelXML is it possible to open a existing file?

        You said you need to open a template file. Usually, that implies making a new file based on the template. And writing back to the same file makes the template not a template any more.

        But, it is possible to write the data back to the original file:

        # open file in read/write mode open(my $xlshandle, '+<', $xlsfile) or die "Can't open '$xlsfile' for +read/write: $!\n"; binmode($xlshandle); # insert code to read spreadsheet # reset handle to beginning of file seek($xlshandle, 0, 0); my $newxls = Spreadsheet::WriteExcelXML->new($xlshandle); # handle, NO +T file # insert code to put updated data into $newxls $newxls->close(); close($xlshandle);

        BUT, it's much safer to:

        my $newxlsfile = $xlsfile . '.new'; my $newxls = Spreadsheet::WriteExcelXML->new($newxlsfile); # file, NOT + handle # insert code to put updated data into $newxls $newxls->close(); close($xlshandle); rename($xlsfile, $xlsfile . '.bak'); rename($newxlsfile, $xlsfile);

        You will still get a file with the same name as the original file, AND will have a back up of the original file.

        Update: added a call to binmode();

Re: Modules for xlsx files
by marto (Cardinal) on Sep 28, 2015 at 12:05 UTC
    "Can't locate object method "SaveAs" via package "Spreadsheet::ParseEx +cel::Workbook" at ./parse_xlsx.pl line 43.

    Where in the documentation did you find this SaveAs method?

      Then how to solve this. i want to open an existing file. Cant possible with Spreadsheet::WriteExcelXML Is there any module to open existing and write the data into .xlsx file and save it.

Re: Modules for xlsx files
by u65 (Chaplain) on Sep 28, 2015 at 14:58 UTC

    If you need to handle both xls and xslx files, take a look at wrapper module Data::Table::Excel which I currently use for reading, but the docs say it can be used for writing, too. The module uses other modules as described from the docs:

    To read and write Excel .xls (2003 and prior) format, we use Spreadsheet::WriteExcel and Spreadsheet::ParseExcel; to read and write Excel .xlsx (2007 format), we use Spreadsheet::ParseXLSX and Excel::Writer::XLSX. If this module gives incorrect results, please check if the corresponding Perl modules are updated. (We switch to Spreadsheet::ParseXLSX from Spreadsheet::XLSX from version 0.5)

    Note: The only reason I haven't used Data::Table::Excel for writing is because I didn't find it until I needed to read xlsx files--I was already using module Spreadsheet::DataToExcel to write xls files, mainly because of the structure of the data I had to write to Excel.