comparison of %2B in perl - perl

I'm just trying to check if a string is equal to "%2B", and if it does I change it to "+".
The problem lies in comparison.
if ($lastItem == "%2B"){
$lastItem = "+";
}
When $lastItem is something completely different (like "hello"), it will still go into the statement. I've been wracking my brain and I just can't tell where I've gone wrong. Does %2B have some special meaning? I'm very new to perl.
Thanks

You need to use eq when comparing strings, or perl will try to convert the string to a number (which will be 0), and you will find such oddities as "a" == 0 to evaluate true. And when comparing two strings, you will of course effectively get if (0 == 0), which is the problem you are describing.
if ($lastItem eq "%2B") {
It is important to note that if you had used use warnings, this problem would have been easier to spot, as this one-liner will demonstrate:
$ perl -wE 'say "yes" if ("foo" == "bar")'
Argument "bar" isn't numeric in numeric eq (==) at -e line 1.
Argument "foo" isn't numeric in numeric eq (==) at -e line 1.
yes

I think you really want the following:
use URI::Escape qw( uri_unescape );
my $unescaped_last_item = uri_unescape($escaped_last_item);
URI::Escape
Please use use strict; use warnings;!

Another example where turning on use warnings would have made it simpler to work out what was wrong.
$ perl -Mwarnings -e'$l = "x"; if ($l == "%2B") { print "match\n" }'
Argument "%2B" isn't numeric in numeric eq (==) at -e line 1.
Argument "x" isn't numeric in numeric eq (==) at -e line 1.
match

Related

Perl: undef element in array

I defined some elements in an array:
my #arrTest;
$arrTest[0]=1;
$arrTest[2]=2;
#Result for #arrTest is: (1, undef, 2)
if($arrTest[1]==0)
{
print "one\n";
}
elsif($arrTest[1] == undef)
{
print "undef\n";
}
I think it should print "undef". But it prints "one" here...
Does it mean $arrTest[1]=undef=0?
How can I modify the "if condition" to distinguish the "undef" in array element?
The operator == in the code $arrTest[1] == 0 puts $arrTest[1] in numeric context and so its value gets converted to a number if needed, as best as the interpreter can do, and that is used in the comparison. And when a variable in a numeric test hasn't been defined a 0 is used so the test evaluates to true (the variable stays undef).
Most of the time when this need be done we get to hear about it (there are some exceptions) -- if we have use warnings; that is (best at the beginning of the program)† Please always have warnings on, and use strict. They directly help.
To test for defined-ness there is defined
if (not defined $arrTest[1]) { ... }
† A demo
perl -wE'say "zero" if $v == 0; say $v; ++$v; say $v'
The -w enables warnings. This command-line program prints
Use of uninitialized value $v in numeric eq (==) at -e line 1.
zero
Use of uninitialized value $v in say at -e line 1.
1
Note how ++ doesn't warn, one of the mentioned exceptions.

Why does "bob" == "godzilla" in Perl?

In Perl class today, a student turned in an assignment which vexes me. We are studying ARGV, but the result was not what I expected. His program (meme.pl) was:
#!/usr/bin/perl
$A = $ARGV[0];
chomp($A);
if ($A == "godzilla"){
print "$A\n";
}
else {
print "We need a monster's name\n";
}
If I type:
% ./meme.pl bob
the result is
% bob
So the variable assignment works, and but the condition ($A == "godzilla") is true no matter what is typed on the command line. I expected that since $ARGV[0] is "bob" and $A=$ARGV[0], then it should not true that $A="godzilla."
What am I missing? I have combed through this code for hours, and I know I am just overlooking some small thing.
Use eq, not ==, to test string equality:
if ($A eq "godzilla"){
More information is available at perldoc perlop.
Note: Adding use strict; and use warnings; to the top of your script would have led you in the right direction.
use strict; and use warnings; should be on...instant F in my book.
But no...evaluations of strings using "==" evaluate all strings - except those that start with a number like '123bob' (see comment below) - as numerical 0. That is why it is evaluating to true - it's "turning into" the statement 0 == 0. use warnings; would have told you something was up.
As many have said - use eq for strings.
More evidence and options can be found here: (http://perlmeme.org/howtos/syntax/comparing_values.html)
The pertinent excerpt (example program):
#!/usr/bin/perl
use strict;
use warnings;
my $string1 = 'three';
my $string2 = 'five';
if ($string1 == $string2) {
print "Equal\n";
} else {
print "Not equal\n";
}
From the above example, you would get warning messages and both strings would evaluate to zero:
Argument "five" isn't numeric in numeric eq (==) at ./test.pl line 8.
Argument "three" isn't numeric in numeric eq (==) at ./test.pl line 8.
Equal
You aren't getting those warnings...just the "Equal", thanks to the absence of use warnings; at the top of your - errr...your student's...cough... - code. ;)
When you are comparing strings, you must use "eq" instead of "==". So replace
($A == "godzilla")
by
($A eq "godzilla")
What the others said is correct about using eq to compare strings. However, the test passes, because when compared numerically with == the string 'bob' and the string 'godzilla' both evaluate to 0, so the test passes and you get bob.

Warnings on equality operators

Has something changed in Perl or has it always been this way, that examples like the second ($number eq 'a') don't throw a warning?
#!/usr/bin/env perl
use warnings;
use 5.12.0;
my $string = 'l';
if ($string == 0) {};
my $number = 1;
if ($number eq 'a') {};
# Argument "l" isn't numeric in numeric eq (==) at ./perl.pl line 6.
Perl will be try to convert a scalar to the type required by the context where it is used.
There is a valid conversion from any scalar type to a string, so this is always done silently.
Conversion to a number is also done silently if the string passes a looks_like_number test (accessible through Scalar::Util). Otherwise a warning is raised and a 'best guess' approximation is done anyway.
my $string = '9';
if ( $string == 9 ) { print "YES" };
Converts the string silently to integer 9, the test succeeds and YES is printed.
my $string = '9,8';
if ( $string == 9 ) { print "YES" };
Raises the warning Argument "9,8" isn't numeric in numeric eq (==), converts the string to integer 9, the test succeeds and YES is printed.
To my knowledge it has always been this way, at least since v5.0.
It has been that way.
In the first if, l is considered to be in numeric context. However, l cannot be converted to a number. Therefore, a warning is emitted.
In the second if, the number 1 is considered to be in string context. Therefore the number 1 is converted to the string '1' before comparison and hence no warnings are emitted.
Did you use a lowercase "L" on purpose? It's often hard to tell the difference between a lowercase "L" and one. You would have answered your own question if you had used a one instead.
>perl -wE"say '1' == 0;"
>perl -wE"say 1 eq 'a';"
>
As you can see,
If one needs a number, Perl will convert a string to a number without warning.
If one needs a string, Perl will convert a number to a string without warning.
Very consistent.
You get a warning when you try to convert a lowercase L to a number, but how is that surprising?

What's wrong with my perl script?

Below is my code, basically if the answer is "Y" then the script runs a message if it's something else then it closes.
#! usr/bin/perl
print "Do you wish to run Program? [Y/N]:";
$answer = <>;
if($answer == "Y") {
print "COOOL\n";
} else {
system "exit"
}
Perl will tell you exactly what the problem is, if you ask it. Just add "use warnings" to your code.
#!/usr/bin/perl
use warnings;
print "Do you wish to run Program? [Y/N]:";
$answer = <>;
if($answer == "Y") {
print "COOOL\n";
} else {
system "exit"
}
Then running it, gives:
$ ./y
Do you wish to run Program? [Y/N]:Y
Argument "Y" isn't numeric in numeric eq (==) at ./y line 6, <> line 1.
Argument "Y\n" isn't numeric in numeric eq (==) at ./y line 6, <> line 1.
COOOL
It's even better if you add "use diagnostics" as well.
$ ./y
Do you wish to run Program? [Y/N]:Y
Argument "Y" isn't numeric in numeric eq (==) at ./y line 7, <> line 1 (#1)
(W numeric) The indicated string was fed as an argument to an operator
that expected a numeric value instead. If you're fortunate the message
will identify which operator was so unfortunate.
Argument "Y\n" isn't numeric in numeric eq (==) at ./y line 7, <> line 1 (#1)
COOOL
Programming in Perl is far easier if you let Perl help you find your errors.
Remove newline. == is for numerical equality, for string you need eq.
chomp($answer);
if($answer eq "Y") {
When you wonder what's going on, start tracing your input. Ensure it is what you think it is:
#!/usr/bin/perl
use strict;
use warnings;
print "Do you wish to run Program? [Y/N]:";
$answer = <>;
print "Answer is [$answer]\n";
Since you put the braces around the variable, you'll notice any extra whitespace. You should see extra stuff in $answer:
Answer is [Y
]
That's your clue that you need to do something to handle that.
And, strict and warnings help you find problems before they are problems.
Probably it will be better to use Term::Prompt or IO::Prompt. Don't reinvent the wheel :)
use IO::Prompt;
prompt -yn, 'Do you wish to run Program?' or exit;
You have newline character, chomp $answer
and $answer eq "Y"
You are using a numerical == to compare your strings.
You probably want to use "eq":
if($answer eq "Y") {
print "COOOL\n";
} else {
system "exit"
}
And as others have suggested you'll want to remove the newline at the end. Use chomp.
Besides the chomp/chop and eq vs ==, you also need to keep in mind the case of the answer. You are testing for UPPERCASE 'Y', I'm willing to bet you are entering lowercase 'y' and they are not equal. I would suggest using:
if (($answer eq 'y') || ($answer eq 'Y')) {
or use uc.

Why does defined sdf return true in this Perl example?

I tried this example in Perl. Can someone explain why is it true?
if (defined sdf) { print "true"; }
It prints true.
sdf could be any name.
In addition, if there is sdf function defined and it returns 0, then it does not print anything.
print (sdf); does not print sdf string but
if (sdf eq "sdf")
{
print "true";
}
prints true.
The related question remains if sdf is a string. What is it not printed by print?
sdf is a bareword.
perl -Mstrict -e "print qq{defined\n} if defined sdf"
Bareword "sdf" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.
For more fun, try
perl -Mstrict -e "print sdf => qq{\n}"
See Strictly speaking about use strict:
The subs aspect of use strict disables the interpretation of ``bare words'' as text strings. By default, a Perl identifier (a sequence of letters, digits, and underscores, not starting with a digit unless it is completely numeric) that is not otherwise a built-in keyword or previously seen subroutine definition is treated as a quoted text string:
#daynames = (sun, mon, tue, wed, thu, fri, sat);
However, this is considered to be a dangerous practice, because obscure bugs may result:
#monthnames = (jan, feb, mar, apr, may, jun,
jul, aug, sep, oct, nov, dec);
Can you spot the bug? Yes, the 10th entry is not the string 'oct', but rather an invocation of the built-in oct() function, returning the numeric equivalent of the default $_ treated as an octal number.
Corrected: (thanks #ysth)
E:\Home> perl -we "print sdf"
Unquoted string "sdf" may clash with future reserved word at -e line 1.
Name "main::sdf" used only once: possible typo at -e line 1.
print() on unopened filehandle sdf at -e line 1.
If a bareword is supplied to print in the indirect object slot, it is taken as a filehandle to print to. Since no other arguments are supplied, print defaults to printing $_ to filehandle sdf. Since sdf has not been opened, it fails. If you run this without warnings, you do not see any output. Note also:
E:\Home> perl -MO=Deparse -e "print sdf"
print sdf $_;
as confirmation of this observation. Note also:
E:\Home> perl -e "print asdfg, sadjkfsh"
No comma allowed after filehandle at -e line 1.
E:\Home> perl -e "print asdfg => sadjkfsh"
asdfgsadjkfsh
The latter prints both strings because => automatically quotes strings on the LHS if they consist solely of 'word' characters, removing the filehandle interpretation of the first argument.
All of these examples show that using barewords leads to many surprises. You should use strict to avoid such cases.
This is a "bareword". If it is allowed, it has the value of "sdf", and is therefore not undefined.
The example isn't special:
telemachus ~ $ perl -e 'if (defined sdf) { print "True\n" };'
True
telemachus ~ $ perl -e 'if (defined abc) { print "True\n" };'
True
telemachus ~ $ perl -e 'if (defined ccc) { print "True\n" };'
True
telemachus ~ $ perl -e 'if (defined 8) { print "True\n" };'
True
None of those is equivalent to undef which is what defined checks for.
You might want to check out this article on truth in Perl: What is Truth?
defined returns true if the expression has a value other than the undefined value.
the defined function returns true unless the value passed in the argument is undefined. This is useful from distinguishing a variable containing 0 or "" from a variable that just winked into existence.