Why can't I match my string from standard input in Perl? - perl

Why will my script not work correctly?
I follow a YouTube video and worked for the guy.
I am running Perl on Windows using ActiveState ActivePerl 5.12.2.1202
Here is my tiny tiny code block.
print "What is your name?\n";
$name = <STDIN>;
if ($name eq "Jon") {
print "We have met before!\n";
} else {
print "We have not met before.\n";
}
The code automatically jumps to the else statement and does not even check the if statement.

The statement $name = <STDIN>; reads from standard input and includes the terminating newline character "\n". Remove this character using the chomp function:
print "What is your name?\n";
$name = <STDIN>;
chomp($name);
if ($name eq "Jon") {
print "We have met before!\n";
} else {
print "We have not met before.\n";
}

The trick in programming is to know what your data are. When something's not acting like you expect, look at the data to see if they are what you expect. For instance:
print "The name is [$name]\n";
You put the braces around it so you can see any extra whitespace that might be there. In this case, you would have seen:
The name is [Jon
]
That's your clue that there is extra stuff. Since the eq has to match exactly, it fails to match.
If you're just starting with Perl, try Learning Perl. It's much better than random videos from YouTube. :)

When you read the name standard input as $name = <STDIN>;
$name will have a trailing newline. So if I enter foo , $name will actually have foo\n.
To get rid of this newline you an make use of the chomp function as:
chomp($name = <STDIN>);

Related

Validating phone number using perl

I want to validate phone number with following conditions:
length should be 10 digits
should start with 7 or 8 or 9
If it does not meet these requirements, we should fail the number.
I have tried the following:
print "Enter phone number: \n";
$name=<>;
chomp $name;
if (length($name)==10 && $name==~ m{/[^7-9]/}){
print "$name is valid \n";
}
else {
print "$name is not valid \n";
}
It might be worth explaining what was wrong with your original version. You have two checks. The first one (length($name)==10) is fine. The problems are with the second one.
$name==~ m{/[^7-9]/}
There are three problems here. Firstly, you're using the wrong operator. You know you need to bind your variable ($name) to your match operator, but the binding operator is =~ not ==~. But, unfortunately, your operator isn't wrong enough to throw a syntax error. Perl will interpret it as a numeric comparison (==) followed by a bitwise negation (~). That's certainly not what you want!
Your second problem is with the match operator. It looks like you know that the match operator is m/.../ and that you also know you can choose an alternative delimiter for the match operator - you've picked m{...}. But you shouldn't nest those delimiters. When you use m{/.../} you're are looking for two literal characters / in your string.
Finally, there's a problem with your actual regex. You want the string to start with 7, 8 or 9. Putting those digits in a character class ([7-9]) is a good idea. But you shouldn't also place the start of string anchor (^) inside the character class. At the start of a character class, ^ has a different meaning - it means "not one of the characters listed here". So your character class ends up meaning the exact opposite of what you wanted it to mean.
Your match expression should have looked like this:
$name =~ m{^[7-9]}
Making your complete code:
print "Enter phone number: \n";
$name = <>;
chomp $name;
if (length($name) == 10 and $name =~ m{^[7-9]}) {
print "$name is valid \n";
}
else {
print "$name is not valid \n";
}
(I did a little tidying - adding some whitespace around operators and switching && for and as is has lower precedence. You might also consider revising your variable name. $name is not a great name for a variable that contains a phone number!)
I would just use a single regex here:
^[789][0-9]{9}$
This avoids having to spread your validation logic across a few places.
if ($name =~ m{/^[789][0-9]{9}$/}){
print "$name is valid \n";
}
else {
print "$name is not valid \n";
}

Command line arguments are not working

I am using Perl for the first time.
I am writing two scripts, and one of those is being called from the other.
While I am passing arguments from user input it's giving an error, but if I hard code the values it works fine.
Please advise how to solve.
Code:
script.pl
use warnings;
my ($choice);
print("Hello!\n");
print("If you want to Generate Add, enter 1.\n");
print("If you want to exit,enter 2.\n");
$choice = <>;
chomp($choice);
if ($choice eq "1") {
print "Please enter 1st argument:";
$inputFile = <STDIN>;
print "Please enter 2nd argument:";
$outputFile = <STDIN>;
system($^X, "generateLdifAdd.pl", $inputFile, $outputFile);
}
elsif ($choice eq "2") {
exit();
}
else {
print("$choice is an invalid response.\n");
}
You probably need to chomp your input:
chomp($inputFile = <STDIN>);
chomp($outputFile = <STDIN>);
Also, don't forget to include use strict; at the top of every script along with use warnings;.
Someone's already mentioned needing to chomp your reads from STDIN.
Would I be right in thinking you've done a print on the values you got, and they're all looking good?
Can I suggest the next port of call is to check what command line you're passing to your second script?
I would suggest as simple as:
print "$^X generateLdifAdd.pl $inputFile $outputFile\n";
Check that looks right to you - gotchas might be that your 'other' script isn't in the path. Or that it's not correctly parsing your command line arguments. (You don't give an example, so it's hard to say). This would have also highlighted the problem with not using chomp - that your args contain linefeeds.

perl exact string match

I have following Perl code to prompt user for yes/no answer. If the user enters anything else than yes/no, keep prompting. No other word is acceptable. I don't know why this code doesn't work. I tested with answer "noooooo" and I was expecting it to prompt again but it does not enter the while loop.
Can anyone help find my mistake here?
#files = A, B, C;
foreach $c (#files) {
if (-e $c ) {
print " $c already exists. Do you want to overwrite? (yes or no): ";
chomp ($file_yes_no = <STDIN>);
while ($file_yes_no !~ m/yes{1}|no{1}/i ) {
print "$c already exists. Do you want to overwrite? (yes or no): ";
chomp ($file_yes_no = <STDIN>);
}
if ($file_yes_no =~ m/yes/i ) {
if (system ("cp -f /home/old_path/ /home/new_path/ == 0) {
print "$c successfully copied;
} else {
die "Error: Copy failed, Check the message above";
}
}
else { print "No files copied\n; }
I would just use the string equality operator eq instead of a regex.
if( $file_yes_no eq 'yes' ) ...
If I wanted it case insensitive I'd first convert to lowercase with lc.
The problem with your regex is it will happily match any string containing the letters yes sequentially. If you wish, you can match the start and end of the string like this:
if ($file_yes_no =~ m/^yes$/i ) ...
But I personally prefer the first option.
Oh, I missed the first part... Hmmmm. Same deal, if you must use regex.
m/^(yes|no)$/i
Once again I'd be more inclined to avoid regex
You should use following Perl regular expression for matching only yes or no (case insensitive):
m/^(yes|no)$/i
For yes only, use:
m/^yes$/i
Because you're using a regular expression. You could write the regular expression to match the beginning or end of the string ... like this:
while( $file_yes_no !~ /^(yes|no)$/ ) {
The ^ and $ are the beginning and end of the string. Also you can omit the m.
Or you could just check the values explicitly:
while( $file_yes_no ne "yes" and $file_yes_no ne "no" ) {
Also you have a typo in your system command but I'm assuming that was just copying it here. You really shouldn't branch out to a shell for that. Look into File::Copy which gives you a copy function

Difficulty with Logic parsing ICAL feed

I have been working on a code that will parse event information from an Ical feed. It is a huge block of data that I want to divide by key term. I need it to be done in an orderly way. I tried indexing the key terms and then having the program print what is between those indexes. However for some reason it became in infinite loop that printed all the data. I don't know how to fix it. DO NOT RUN MY CODE IT KEEPS FREEZING MY COMPUTER. I was hoping someone could show me what my problem is.
DO NOT RUN THIS PROGRAM
use strict;
use warnings;
use LWP::Simple;
use HTML::TreeBuilder;
use HTML::FormatText;
my $URL= get("https://www.events.utoronto.ca/iCal.php?ical=1&campus=0&
+sponsor%5B%5D=&audience%5B%5D=&category%5B%5D=");
my $Format=HTML::FormatText->new;
my $TreeBuilder=HTML::TreeBuilder->new;
$TreeBuilder->parse($URL);
my $Parsed=$Format->format($TreeBuilder);
open(FILE, ">UOTSUMMER.txt");
print FILE "$Parsed";
close (FILE);
open (FILE, "UOTSUMMER.txt");
my #array=<FILE>;
my $string ="#array";
my $offset = 0; # Where are we in the string?
my $numResults = 0;
while (1) {
my $idxSummary = index($string, "SUMMARY", $offset);
my $result = "";
my $idxDescription = index ($string, "DESCRIPTION", $offset);
my $result2= "";
if ($idxSummary > -1) {
$offset = $idxSummary + length("SUMMARY");
my $idxDescription = index($string, "DESCRIPTION", $offset);
if ($idxDescription == -1) {
print "(Data malformed: missing DESCRIPTION line.)\n";
last;
}
if ($idxDescription > -1) {
$offset = $idxDescription+ length("DESCRIPTION");
my $idxLocation= index($string, "LOCATION", $offset);
if ($idxLocation == -1) {
print "(Data malformed: missing LOCATION line.)\n";
last;
}
my $length = $idxDescription - $offset;
my $length2= $idxLocation - $offset;
$result = substr($string, $offset, $length);
$result2= substr ($string, $offset, $length2);
$offset = $idxDescription + length("DESCRIPTION");
$result =~ s/^\s+|\s+$//g ; # Strip leading and trailing white space, including newlines.
$result2 =~ s/^\s+|\s+$//g ;
$numResults++;
} else {
print "(All done. $numResults result(s) found.)\n";
last;
}
open (FILE2, "UOT123.txt")
print FILE2 "TITLE: <$result>\n DESCRIPTION: <$result2>\n";
Any guidance you may have will be greatly appreciated! Thanks!
I was so inspired by your warnings that I had to run it. I even installed the required modules to do so. Your computer is probably just getting bogged down by the endless loop, not really crashing.
Looking at your code, the problem is almost certainly your indexing. As it stands now, your looping logic is kind of a mess. Your best bet would be to rethink how you are doing this. Rather than using all of this logic, try making the loop dependent on going through the file. That way, it will be much harder to make an endless loop. Also, regular expressions will make this job much simpler. This probably doesn't do exactly what you want, but it is a start:
while ($string =~ m/SUMMARY(.+?)DESCRIPTION(.+?)(?=SUMMARY|$)/gcs)
{
print "summary is: \n\n $1 \n\n description is: \n\n $2 \n\n";
}
Some other quick points:
Writing to a file and then opening it and reading the content back out again at the beginning doesn't make much sense. You already have exactly what you want in $Parsed.
If you just want to print a variable by itself, don't put it in quotes. This adds a lot of overhead.
Perhaps the following will assist you with your parsing task:
use Modern::Perl;
use LWP::Simple qw/get/;
use HTML::Entities;
my $html = get 'https://www.events.utoronto.ca/iCal.php?ical=1&campus=0&+sponsor%5B%5D=&audience%5B%5D=&category%5B%5D=';
while ( $html =~ /(Summary:\s*[^\n]+)\s*(Description:\s*[^\n]+)/gi ) {
say decode_entities($1) . "\n" . decode_entities($2);
}
Sample Output:
SUMMARY:Learning Disabilities Parent Support Group
DESCRIPTION: Dates: Thursdays: May 24, June 21, and July 19
SUMMARY:"Reading to Write"
DESCRIPTION: Leora Freedman, Coordinator, English Language Learning Program, Faculty of Arts & Science
SUMMARY:The Irish Home Rule Bill of 1912: A Centennial Symposium
DESCRIPTION: One-day symposium presented by the Celtic Studies Program, St. Michael's College
If html entities are OK within the text, you can omit using HTML::Entities and the decode_entities($1) notation, else you may get results like the following:
DESCRIPTION: Leora Freedman, Coordinator, English Language Learning Program, Faculty of Arts & Science
Hope this helps!

Perl comparison operation between a variable and an element of an array

I am having quite a bit of trouble with a Perl script I am writing. I want to compare an element of an array to a variable I have to see if they are true. For some reason I cannot seem to get the comparison operation to work correctly. It will either evaluate at true all the time (even when outputting both strings clearly shows they are not the same), or it will always be false and never evaluate (even if they are the same). I have found an example of just this kind of comparison operation on another website, but when I use it it doesn't work. Am I missing something? Is the variable type I take from the file not a string? (Can't be an integer as far as I can tell as it is an IP address).
$ipaddress = '192.43.2.130'
if ($address[0] == ' ')
{
open (FH, "serverips.txt") or die "Crossroads could not find a list of backend servers";
#address = <FH>;
close(FH);
print $address[0];
print $address[1];
}
for ($i = 0; $i < #address; $i++)
{
print "hello";
if ($address[$i] eq $ipaddress)
{print $address[$i];
$file = "server_$i";
print "I got here first";
goto SENDING;}
}
SENDING:
print " I am here";
I am pretty weak in Perl, so forgive me for any rookie mistakes/assumptions I may have made in my very meager bit of code. Thank you for you time.
if ($address[0] == ' ')
{
open (FH, "serverips.txt") or die "Crossroads could not find a list of backend servers";
#address = <FH>;
close(FH);
You have several issues with this code here. First you should use strict because it would tell you that #address is being used before it's defined and you're also using numeric comparison on a string.
Secondly you aren't creating an array of the address in the file. You need to loop through the lines of the file to add each address:
my #address = ();
while( my $addr = <FH> ) {
chomp($addr); # removes the newline character
push(#address, $addr);
}
However you really don't need to push into an array at all. Just loop through the file and find the IP. Also don't use goto. That's what last is for.
while( my $addr = <FH> ) {
chomp($addr);
if( $addr eq $ipaddress ) {
$file = "server_$i";
print $addr,"\n";
print "I got here first"; # not sure what this means
last; # breaks out of the loop
}
}
When you're reading in from a file like that, you should use chomp() when doing a comparison with that line. When you do:
print $address[0];
print $address[1];
The output is on two separate lines, even though you haven't explicitly printed a newline. That's because $address[$i] contains a newline at the end. chomp removes this.
if ($address[$i] eq $ipaddress)
could read
my $currentIP = $address[$i];
chomp($currentIP);
if ($currentIP eq $ipaddress)
Once you're familiar with chomp, you could even use:
chomp(my $currentIP = $address[$i]);
if ($currentIP eq $ipaddress)
Also, please replace the goto with a last statement. That's perl's equivalent of C's break.
Also, from your comment on Jack's answer:
Here's some code you can use for finding how long it's been since a file was modified:
my $secondsSinceUpdate = time() - stat('filename.txt')->mtime;
You probably are having an issue with newlines. Try using chomp($address[$i]).
First of all, please don't use goto. Every time you use goto, the baby Jesus cries while killing a kitten.
Secondly, your code is a bit confusing in that you seem to be populating #address after starting the if($address[0] == '') statement (not to mention that that if should be if($address[0] eq '')).
If you're trying to compare each element of #address with $ipaddress for equality, you can do something like the following
Note: This code assumes that you've populated #address.
my $num_matches=0;
foreach(#address)
{
$num_matches++ if $_ eq $ipaddress;
}
if($num_matches)
{
#You've got a match! Do something.
}
else
{
#You don't have any matches. This may or may not be bad. Do something else.
}
Alternatively, you can use the grep operator to get any and all matches from #address:
my #matches=grep{$_ eq $ipaddress}#address;
if(#matches)
{
#You've got matches.
}
else
{
#Sorry, no matches.
}
Finally, if you're using a version of Perl that is 5.10 or higher, you can use the smart match operator (ie ~~):
if($ipaddress~~#address)
{
#You've got a match!
}
else
{
#Nope, no matches.
}
When you read from a file like that you include the end-of-line character (generally \n) in each element. Use chomp #address; to get rid of it.
Also, use last; to exit the loop; goto is practically never needed.
Here's a rather idiomatic rewrite of your code. I'm excluding some of your logic that you might need, but isn't clear why:
$ipaddress = '192.43.2.130'
open (FH, "serverips.txt") or die "Crossroads could not find a list of backend servers";
while (<FH>) { # loop over the file, using the default input space
chomp; # remove end-of-line
last if ($_ eq $ipaddress); # a RE could easily be used here also, but keep the exact match
}
close(FH);
$file = "server_$."; # $. is the line number - it's not necessary to keep track yourself
print "The file is $file\n";
Some people dislike using perl's implicit variables (like $_ and $.) but they're not that hard to keep track of. perldoc perlvar lists all these variables and explains their usage.
Regarding the exact match vs. "RE" (regular expression, or regexp - see perldoc perlre for lots of gory details) -- the syntax for testing a RE against the default input space ($_) is very simple. Instead of
last if ($_ eq $ipaddress);
you could use
last if (/$ipaddress/);
Although treating an ip address as a regular expression (where . has a special meaning) is probably not a good idea.