what's wrong with my exit condition - excel

I have a script which parses an XLSX document and convert it in other format.
In the part where the XLSX file is red, i have an exit condition:
do
{
...
...
...
}
until (uc(trim($worksheet -> get_cell($indexRow, 0) -> value())) eq "");
unfortunately, when I am executing the script, I receive this error:
Can't call method "value" on an undefined value at myfilename.pl line 94
Can anyone give me a hint or explain what what i am doing wrong, please?
by the way, I am a beginner in Perl programming.

the expression
$worksheet -> get_cell($indexRow, 0)
Is not defined. So, no properties can be extracted.
My advice is to re-factor the code this way (but every body have his own advice) (take into considerations spaces in the second condition!):
while (1) # 1 is true
{
...
...
...
# suppose $indexrow has been calculated in the loop.
if ( ! $worksheet -> get_cell($indexRow, 0) ) {
# do something (cell is not defined)
# Cell is empty? (??????? 7 ?)
}
elsif ( ! trim( $worksheet->get_cell($indexRow,0)->value() ) ) {
last; #escape this loop
}
}
Empty string is false, so ! is equivalent to "the string is holding text".
Hope it helps.

Related

How to set an empty or not empty entry for a switch case statement?

I'd like to know to set an entry to validate if a String is empty or not empty in a switch-case statement. Let me show you:
String str = 'value'
switch(str){
case str == '':
println('Entered an empty value')
break
case str != '':
println('Entered value '+str)
break
case 'CoDe':
println('Entered special code '+str)
break
default:
println('Incorrect entry')
break
}
I know how to set an entry with a value (case 3) but I don't know how to set a entry with an empty or not empty string value.
Any help?
I tested it, and I think I have the answer for you.
switch(str) {
case "": // empty
println("Entered an empty value")
break
case "CoDe": // str == CoDe
println("Entered special code "+str)
break
default: // String not empty and not "CoDe"
println("Not empty")
break
}
It works because you have case "" which is the empty string. Meaning that everithing else is not empty. everything else is the default:
If you are not convinced, I'll give you a different example with the same meaning.
if (str.isEmpty()) {
// do something
} else if (!str.isEmpty()) { // <- this if is redundant
// do something else
}
You don't need the second if, because, you enter the else branch only if str is not empty! I appleid the same logic to the case. And it works because case "": in Java is valid.
As commented, what you need in Java is a series of if tests.
In the String class, you can test either:
The string has no characters at all (isEmpty)
The string has no characters OR has only whitespace characters (isBlank)
if ( str.isEmpty() ) { System.out.println( "ERROR - String has no characters." ); }
else if ( str.isBlank() ) { System.out.println( "ERROR - String has only whitespace." ); }
else if ( str.equals( "CoDe" ) ) { System.out.println( "Code received." ); }
else { System.out.println( "ERROR - Incorrect entry." ); }
See that code run live at Ideone.com.
Before that code block, I would add a null-check.
Objects.requireNonNull( str ); // Throw exception if null.
I find the if - else if - else construct in Java to be awkward in terms of readability. The switch syntax is much better at making obvious that a series of possibilities is being tested, mutually-exclusive, with a single result.
I do wish Java offered a cascading test for a series of boolean expressions. I have used such a feature in another language. There I found the cascading-boolean-tests to be quite practical for handling a series of business rules. But, alas, no such feature in Java. If only Brian Goetz were taking my calls.
I do not know Groovy. Perhaps Groovy provides another facility.
Your code has a smell, as str != '' would block all further cases.
I would re-organize it and use straight-forward basic Groovy switch-statement:
String str = 'value'
switch(str){
case 'CoDe':
println "Entered special code $str"
break
case '':
println 'Entered an empty value'
break
case { str != '' && str != null }:
println "Entered value $str"
break
default:
println 'Incorrect entry'
}
prints
Entered value value

stuck at understanding "$worksheet->add_write_handler(qr[\w], \&store_string_widths);" line in the below piece of code

My final goal for my first perl program :To create an excel sheet for reporting purpose and email the sheet as an attachment.
I have reached till creating a csv file. now i wanted to convert this to excel sheet and autofit the content.
I have an example code in our environment,could someone take time to explain each line on the below code, it would be very grateful.
outputfile,urloutputfile,scomoutputfile - are the csv files, now being converted to excel sheets.
Please explain how an element is being passed to the other function also.
my $parser = Text::CSV::Simple->new;
my $workbook = Excel::Writer::XLSX->new($auditxl);
my #totcsvlist;
push(#totcsvlist,$outputfile);
push(#totcsvlist,$urloutputfile);
push(#totcsvlist,$scomoutputfile);
my #data;
my $subject = 'worksheet';
foreach my $totcsvlis (#totcsvlist)
{
undef #data;
chomp($totcsvlis);
if ($totcsvlis eq $outputfile) { $subject="Service Status"; }
if ($totcsvlis eq $urloutputfile) { $subject="URL Status"; }
if ($totcsvlis eq $scomoutputfile) { $subject="SCOM Agent Status"; }
#data = $parser->read_file($totcsvlis);
my $headers = shift #data;
import_data($workbook, $subject, $headers, \#data);
}
$workbook->close();
sub autofit_columns {
my $worksheet = shift;
my $col = 0;
for my $width (#{$worksheet->{__col_widths}}) {
$worksheet->set_column($col, $col, $width) if $width;
$col++;
}
}
sub import_data {
my $workbook = shift;
my $base_name = shift;
my $colums = shift;
my $data = shift;
my $limit = shift || 50_000;
my $start_row = shift || 1;
my $bold = $workbook->add_format();
$bold->set_bold(1);
$bold->set_bg_color('gray');
$bold->set_border();
my $celbor = $workbook->add_format();
$celbor->set_border();
my $worksheet = $workbook->add_worksheet($base_name);
$worksheet->add_write_handler(qr[\w], \&store_string_widths);
my $w = 1;
$worksheet->write('A' . $start_row, $colums, $bold);
my $i = $start_row;
my $qty = 0;
for my $row (#$data) {
$qty++;
$worksheet->write($i++, 0, $row,$celbor);
}
autofit_columns($worksheet);
warn "Convereted $qty rows.";
return $worksheet;
}
sub autofit_columns {
my $worksheet = shift;
my $col = 0;
for my $width (#{$worksheet->{__col_widths}}) {
$worksheet->set_column($col, $col, $width + 5) if $width;
$col++;
}
}
sub store_string_widths {
my $worksheet = shift;
my $col = $_[1];
my $token = $_[2];
return if not defined $token; # Ignore undefs.
return if $token eq ''; # Ignore blank cells.
return if ref $token eq 'ARRAY'; # Ignore array refs.
return if $token =~ /^=/; # Ignore formula
return if $token =~ m{^[fh]tt?ps?://};
return if $token =~ m{^mailto:};
return if $token =~ m{^(?:in|ex)ternal:};
my $old_width = $worksheet->{__col_widths}->[$col];
my $string_width = string_width($token);
if (not defined $old_width or $string_width > $old_width) {
$worksheet->{__col_widths}->[$col] = $string_width;
}
return undef;
}
sub string_width {
return length $_[0];
}
I have tried to search and read modules used in the above code, but over head.
https://github.com/jmcnamara/spreadsheet-writeexcel/blob/master/examples/autofit.pl
-- has similar code and has provided a basic over view. but i would like to understand in detail.
Thank you so much in advance.
Regards,
Kaushik KM
Here is the documentation for the add_write_handler() method call. It says:
add_write_handler( $re, $code_ref )
This method is used to extend the Excel::Writer::XLSX write() method
to handle user defined data.
And later, it says:
The add_write_handler() method take two arguments, $re, a regular
expression to match incoming data and $code_ref a callback function
to handle the matched data
So, here you have a method call that takes two arguments. The first is a regex that tells the object what type of data this new write handler is used for. The second is a reference to the subroutine that should be used as the write handler for data that matches the regex.
The regex you have is qr[\w]. The actual regex bit of that is \w. And that just means "match a word character". The qr is to compile a string into a regex and the [ ... ] is just the delimiter for the regex string (qr/.../ is one of a class of Perl operators that allows you to use almost any character you want as a delimiter).
So, if your object is called on to write some data that contains at least one word character, the subroutine which is given as the second argument is used. But we take a reference to the subroutine.
Elsewhere in your code, you define the store_string_widths() subroutine. Subroutines in Perl are a lot like variables, and that means that they have their own sigil. The sigil for a subroutine is & (like the $ for scalar and # for arrays). You very rarely need the & in modern Perl code, so you won't see it used very often. One place that it is still used, is when we take a reference to a subroutine. You take a reference to any variable by putting a slash in front of the variable's full name (like \#array or \%hash) and subroutines are no different. So \&store_string_widths means "get a reference to the subroutine called store_string_widths()".
You say that this is your first Perl program. I have to say that this feels a little ambitious for your first Perl code. I don't cover references at all in my two-day beginners course and on my intermediate course I cover most references, but on mention subroutine references in passing. If you can understand references enough to get this all working, then I think you're doing really well.

Avoid Depth First Search (DFS) from infinite loop at finding all path of two giving cell

There's a 2D array that I'm going to find all the path from Begin-cell to Goal-cell(no matter shortest/longest).
and there's DFS solution used over the array(graph) and will explore cells (node's).
The c# like pseudocode :
//setin A and B
setAB(beginNode,goalNode);
//collect info from where we was to explore other sub path
//first seen:
visited.add(beginNode);
//wholegrid as a matrix and visited for memory
Search(wholeGrid,visited){
neighborsNodes =getNeighbors(visited.last);
//check in neighbors condition first
foreach(node in neighborsNodes ){
if(visited.contains(node)){continue};
if(node==goalNode)){
visited.add(node);
saveOrShowPath();
visited.remove(node);
break;
}
}
//recursive call here
foreach(node in neighborsNodes ){
if(visited.contains(node)||node==goalNode){continue;};
visited.add(node);
Search(wholeGrid,visited);
visited.remove(node);
}
}
}
Rise of Problem:
For any random start/goal node that will fall in a loop !
The initial code was written for graph not for the grid.
How to fix that if the problem is related to this:
http://en.wikipedia.org/wiki/Iterative_deepening_depth-first_search
or please aware me if the solution itself is wrong.
-thanks in advance.
Everything needs an end. Simplest recursive:
calc(factorial)
{
if (factorial == 0)
return 1;
else
return calc(factorial - 1) * factorial;
}

Identifying date types in Spreadsheet::ParseExcel

We are migrating from a MS Excel OLE based module to Spreadsheet::ParseExcel (or similar). As we have hundreds of programs using our module we'd prefer that we provide a drop in replacement i.e. the data returned is identical.
The problem is dates - using Excel we get a Win32::OLE::Variant object of type VT_DATE. As a workaround we can construct this manually by checking $cell->type() eq 'Date' and returning the object.
The problem is that the type is not reliably set, so we can't always do this.
The Date type is set in two places. This is the logic used in FmtDefault.pm:
if ( ( ( $iFmtIdx >= 0x0E ) && ( $iFmtIdx <= 0x16 ) )
|| ( ( $iFmtIdx >= 0x2D ) && ( $iFmtIdx <= 0x2F ) ) )
{
return "Date";
}
and if this check fails and we get Numeric, then it does a backup check in ParseExcel.pm:
if ( $FmtStr =~ m{^[dmy][-\\/dmy]*$}i ) {
$rhKey{Type} = "Date";
}
However a number of common format strings are not working, for example:
[$-C09]dddd\\,\\ d\\ mmmm\\ yyyy;# i.e. Sunday, 24 January 1982
d/m/yyyy;# i.e. 24/1/1982
I've checked the Excel specification at openoffice.org and also read guides such as http://jonvonderheyden.net/excel/a-comprehensive-guide-to-number-formats-in-excel/#date_code and it seems that the below rule will match a date format string:
A string with d, m, or y characters, which are not between "" or [], not preceded with \ unless it's a \\, and not followed by - or *.
This seems very complicated and error-prone. Is there a better way?
It seems Spreadsheet::ParseExcel::Utility::ExcelFmt() flags a date format under $format_mode so perhaps this logic can be modified to return the flag? But I'd prefer something ready to go without changing the Spreadsheet::ParseExcel modules if possible.
Do you know what columns are supposed to be dates?
In my usage, I do, and convert them with:
$val = $cell->unformatted();
# if it was properly set as a Date cell, the value will be a number of days since 1900 or 1904
# that we can convert to a date, regardless of the format they were shown.
if ( $val =~ /^[0-9]{5}(?:\.[0-9]+)?\z/ ) {
$date = Spreadsheet::ParseExcel::Utility::ExcelFmt( 'YYYY-MM-DD', $val, $wb->{'Flg1904'} );
}
else {
$val = $cell->value();
$val =~ s/^'//;
# try parsing it with Date::Manip, which handles all common formats (see its ParseDateString doc)
use Date::Manip ();
Date::Manip::Date_Init("TZ=GMT","DateFormat=US");
$date = Date::Manip::UnixDate( $val, '%Y-%m-%d' );
}
Update: sounds like you are best off modifying ExcelFmt, something like this (untested):
--- Utility.pm.orig 2014-12-17 07:16:06.609942082 -0800
+++ Utility.pm 2014-12-17 07:18:14.453965764 -0800
## -69,7 +69,7 ##
#
sub ExcelFmt {
- my ( $format_str, $number, $is_1904, $number_type, $want_subformats ) = #_;
+ my ( $format_str, $number, $is_1904, $number_type, $want_subformats, $want_format_mode ) = #_;
# Return text strings without further formatting.
return $number unless $number =~ $qrNUMBER;
## -956,8 +956,14 ##
$result =~ s/^\$\-/\-\$/;
$result =~ s/^\$ \-/\-\$ /;
- # Return color and locale strings if required.
- if ($want_subformats) {
+ # Return format mode and/or color and locale strings if required.
+ if ( $want_subformats && $want_format_mode ) {
+ return ( $result, $color, $locale, $format_mode );
+ }
+ elsif ($want_format_mode) {
+ return ( $result, $format_mode );
+ }
+ elsif ($want_subformats) {
return ( $result, $color, $locale );
}
else {
Be sure to submit it to the maintainer for inclusion in a later release.

perl null or empty detection

I have a hash containing results of matching substrings. I want to print a message if there is no matching between the string. I've tried the following and it didn't work.
foreach (keys %d) {
if ($_ eq "") {
print "no matches"; # and i've tried (if defined $_
} else {
print "$_\n";
}
}
the % d is filled this way (it contains matched substrings) :
foreach (my $i=0;$i<length($seq1)-$k;$i+=1) {
my $common=substr($seq1,$i,$k);
if ($seq2=~/$common/) {
$d{$common}++;
}
}
I think I finally see what you are trying to accomplish. You think that checking if the keys in %d equal the empty string, then there were no matches in your loop. This is false. If there are no matches, then there are no keys, and the loop will never execute.
Unfortunately, you cannot check if %d contains no values that way. You need something like:
unless (%d) {
print "No matches\n";
} else {
print "$_\n" for keys %d;
}
You have an iteration over all existing keys and check if the are an empty string, that's I guess not what you want.
Try
if (defined $d{$_})
or if it is set to "" then
if ($d{$_} eq "")
To be more helpfull, one would have to know how your hash is filled.
You need to initialize also the non matching values. In your code you can add an
if ($seq2=~/$common/) {
$d{$common}++;
}
else
{ $d{$common} = 0 unless (exists($d{common})); }
and then check
if ($d{$_} > 0)

Resources