Rewrite existing xls files using Perl - perl

I am trying to write data in excel files which is already exists, but the code I tried is creating a new sheet and erasing the old sheet with the data. This the code I use
#!/usr/bin/perl –w
use strict;
use Spreadsheet::WriteExcel;
# Create a new Excel file
my $FileName = 'Report.xls';
my $workbook = Spreadsheet::WriteExcel->new($FileName);
# Add a worksheet
my $worksheet1 = $workbook->add_worksheet(); #<- My Doubt
# Change width for only first column
$worksheet1->set_column(0,0,20);
# Write a formatted and unformatted string, row and column
# notation.
$worksheet1->write(0,0, "Hello");
$worksheet1->write(1,0,"HI");
$worksheet1->write(2,0,"1");
$worksheet1->write(3,0,"2"); `
How can I assign the current sheet to $worksheet1. And one more thing is I need to read specific cell from which is already exist.
Please give me some guidance .Thank you

You cannot open an existing a spreadsheet with Spreadsheet::WriteExcel and update it like that. You first want to open it using Spreadsheet::ReadExcel along with an output sheet which you open with WriteExcel. Then, you read the input file, write out existing cells, sheets etc, and make whatever edits/updates/insertions you are going to make. Then, you can close both files, remove the previous, and rename the new one (optionally backing up the previous version).
You can only really edit/change a given Excel file without going through this process by opening it using Win32::OLE, but for that you are most certainly going to need to be on a Windows system (I am not sure about the state of Wine), and this is not something you want to do on a server.
You can think of creating a file with Spreadsheet::WriteExcel as similar to opening a file with open my $fh, '>', 'output.file' ... output.file will be clobbered.
Note the line:
my $fh = FileHandle->new('>'. $self->{_filename});
in Spreadsheet::WriteExcel::Workbook->new.

Related

Reading Xlsx from another Xlsx file

I have few Xlsx files say X.xlsx,Y.xlsx,Z.XLSX and I kept those three Xlsx files in another xlsx file say A.xlsx. Now I want to ready the content in the three xlsx files(x,y,z) at a time through A.xlsx.
Can any one help me on this.
Thanks in advance
This is easy on Windows if your target machine also has Microsoft Excel installed.
Use the Win32::OLE module to create an instance of Excel, open your master file A.xlsx and then iterate over its ->{OLEObjects} property:
#!perl
use strict;
use warnings;
use Win32::OLE 'in';
$ex = Win32::OLE->new('Excel.Application') or die "oops\n";
my $Axlsx = $ex->Open('C:\\Path\\To\\A.xlsx');
my $i=0;
for my $embedded (in $Axlsx->OLEObjects) {
$embedded->Object->Activate();
$embedded->Object->SaveAs("test$i++.xlsx");
$embedded->Object->Close;
}
After saving them, you can treat them as normal Excel files. Alternatively, you can work directly with $embedded->Object, but as you haven't told us what exactly you need to do, it's hard to give specific advice.
See also Save as an Excel file embedded in another Excel file

How to run macro from specific workbook w/ Win32::OLE

(Using Win32:OLE w/ Perl)
Is there a way to specify which workbook an Excel macro is run from if multiple workbooks are already open?
I am using the following code, but it sometimes fails if more than one Excel workbook is open. I know there is an obvious solution to just close the other workbooks, but my work often involves using another Excel workbook (which is a hassle to keep closing and reopening). So I am hoping there is a way to specify which workbook to run the macro from (perhaps similar to how one specifies an additional parameter such as the title for Win32::GUI).
Here is an example of what currently just works with one workbook open:
use Win32::OLE;
my $excel = Win32::OLE->GetActiveObject('Excel.Application');
$excel->Run("macroName");
The macro I am running exports all the sheets of the current workbook, so it is important that it is run from the correct workbook. I checked out this question (How do I use Perl to run a macro in an **already open** Excel workbook) but it doesn't seem to address how to select which macro the workbook is run from.
I was also reading through this article (http://docs.activestate.com/activeperl/5.6/faq/Windows/ActivePerl-Winfaq12.html), but it didn't seem to show a way how to run a macro from a specific workbook. Thinking a bit more, I could see not being able to specify which workbook to run from being an issue if two workbooks have the same macro name, but for some reason, these do different things.
Any suggestions?
You need to select the worksheet you want within the workbook. e.g. open a file, select the second workbook (by name) and activate it:
#!/usr/bin/perl
use strict;
use warnings;
use Win32::OLE;
use Win32::OLE::Const 'Microsoft Excel';
my $excelfile = 'd:\\Book1.xls';
my $Excel = Win32::OLE->GetActiveObject('Excel.Application')
|| Win32::OLE->new('Excel.Application');
$Excel->{'Visible'} = 1;
my $Book = $Excel->Workbooks->Open("$excelfile") or die "$!";
my $Sheet = $Book->Worksheets("Sheet2");
$Sheet->Activate();
This is a great reference for working with Win32::OLE – perl Win32::OLE cheat sheet

How to handle excel using OLE module?

Friends, I wrote a Perl script to convert a set of CSV files into spreadsheet
format using Spreadsheet::WriteExcel. After some research I came to
conclusion that there is no option to fix column width as Auto-fit option.
So what I'm doing is in the same script I've opened that XLS file using Win32::OLE
module, while doing this I got an error message
Can't use an undefined value as a HASH reference
Corresponding code is:
# spread sheet creation
my $workbook = Spreadsheet::WriteExcel->new($file_name);
# ...
my $worksheet = $workbook->add_worksheet($work_sheet_name);
# ...
$worksheet->write($rowNum, $j,$_,$default_format);
after these steps I have some more lines in the same script:
my $Excel = Win32::OLE->GetActiveObject('Excel.Application')
|| Win32::OLE->new('Excel.Application');
$Excel->{'Visible'} = 0; #0 is hidden, 1 is visible
$Excel->{DisplayAlerts}=1; #0 is hide alerts
# Open File and Worksheet
my $return_file_name="C:\\Users\\admin\\Desktop\\Report_Gen\\$file_name";
print ">>$return_file_name<<";
my $Book = $Excel->Workbooks->Open($return_file_name); # open Excel file
foreach my $Sheet (in $Book->Sheets) {
my $LastCol = $Sheet->UsedRange->Find({What=>"*",
SearchDirection=>xlPrevious,
SearchOrder=>xlByColumns})->{Column}; # mentioned error is from this line
my $mylastcol = 'A';
for (my $m=1;$m<$LastCol;$m++) {$mylastcol++;}
my #columnheaders = ('A:'.$mylastcol);
foreach my $range (#columnheaders){
$Sheet->Columns($range)->AutoFit();
}
I wrote a Perl script to convert a set of CSV files into spreadsheet format using Spreadsheet::WriteExcel. After some research I came to conclusion that there is no option to fix column width as Auto-fit option.
Autofit is a runtime option in Excel and so it isn't possible to create it via the file format using Spreadsheet::WriteExcel.
However, the Spreadsheet::WriteExcel docs contain an example of how to simulate autofit with an explanation of some of the issues involved.

How do I delete a random value from an array in Perl?

I'm learning Perl and building an application that gets a random line from a file using this code:
open(my $random_name, "<", "out.txt");
my #array = shuffle(<$random_name>);
chomp #array;
close($random_name) or die "Error when trying to close $random_name: $!";
print shift #array;
But now I want to delete this random name from the file. How I can do this?
shift already deletes a name from the array.
So does pop (one from the beginning, one from the end) - I would suggest using pop as it may be more efficient and being a random one, you don't care which on you use.
Or do you need to delete it from a file?
If that's the case, you need to:
A. get a count of names inside a file (if small, read it all in memory using File::Slurp, if large, either read it line-by-line and count or simply execute wc -l $filename command via backticks.
B. Generate a random # from 1 to <$ of lines> (say, $random_line_number
C. Read the file line by line. For every line read, WRITE it to another temp file (use File::Temp to generate temp files. Except do NOT write the line numbered $random_line_number to text file
D. Close temp file and move it instead of your original file
If the list contains filenames and you need to delete the file itself (the random file), use unlink() function. Don't forget to process return code from unlink() and, like with any IO operation, print error message containing $! which will be the text of system error on failure.
Done.
D.
When you say "delete this … from the list" do you mean delete it from the file? If you simply mean remove it from #array then you've already done that by using shift. If you want it removed from the file, and the order doesn't matter, simply write the remaining names in #array back into the file. If the file order does matter, you're going to have to do something slightly more complicated, such as reopen the file, read the items in in order, except for the one you don't want, and then write them all back out again. Either that, or take more notice of the order when you read the file.
If you need to delete a line from a file (its not entirely clear from your question) one of the simplest and most efficient ways is to use Tie::File to manipulate a file as if it were an array. Otherwise perlfaq5 explains how to do it the long way.

How can I modify an existing Excel workbook with Perl?

With Spreadsheet::WriteExcel, I can create a new workbook, but what if I want to open an existing book and modify certain columns? How would I accomplish that?
I could parse all of the data out of the sheet using Spreadsheet::ParseExcel then write it back with new values in certain rows/columns using Spreadsheet::WriteExcel, however. Is there a module that already combines the two?
Mainly I just want to open a .xls, overwrite certain rows/columns, and save it.
Spreadsheet::ParseExcel will read in existing excel files:
my $parser = Spreadsheet::ParseExcel->new();
# $workbook is a Spreadsheet::ParseExcel::Workbook object
my $workbook = $parser->Parse('Book1.xls');
But what you really want is Spreadsheet::ParseExcel::SaveParser, which is a combination of Spreadsheet::ParseExcel and Spreadsheet::WriteExcel. There is an example near the bottom of the documentation.
If you have Excel installed, then it's almost trivial to do this with Win32::OLE. Here is the example from Win32::OLE's own documentation:
use Win32::OLE;
# use existing instance if Excel is already running
eval {$ex = Win32::OLE->GetActiveObject('Excel.Application')};
die "Excel not installed" if $#;
unless (defined $ex) {
$ex = Win32::OLE->new('Excel.Application', sub {$_[0]->Quit;})
or die "Oops, cannot start Excel";
}
# get a new workbook
$book = $ex->Workbooks->Add;
# write to a particular cell
$sheet = $book->Worksheets(1);
$sheet->Cells(1,1)->{Value} = "foo";
# write a 2 rows by 3 columns range
$sheet->Range("A8:C9")->{Value} = [[ undef, 'Xyzzy', 'Plugh' ],
[ 42, 'Perl', 3.1415 ]];
# print "XyzzyPerl"
$array = $sheet->Range("A8:C9")->{Value};
for (#$array) {
for (#$_) {
print defined($_) ? "$_|" : "<undef>|";
}
print "\n";
}
# save and exit
$book->SaveAs( 'test.xls' );
undef $book;
undef $ex;
Basically, Win32::OLE gives you everything that is available to a VBA or Visual Basic application, which includes a huge variety of things -- everything from Excel and Word automation to enumerating and mounting network drives via Windows Script Host. It has come standard with the last few editions of ActivePerl.
There's a section of the Spreadsheet::WriteExcel docs that covers Modifying and Rewriting Spreadsheets.
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.
You can read and rewrite an Excel file using the Spreadsheet::ParseExcel::SaveParser module which is a wrapper around Spreadsheet::ParseExcel and Spreadsheet::WriteExcel. It is part of the Spreadsheet::ParseExcel package.
There's an example as well.
The Spreadsheet::ParseExcel::SaveParser module is a wrapper around Spreadsheet::ParseExcel and Spreadsheet::WriteExcel.
I recently updated the documentation with, what I hope, is a clearer example of how to do this.