How do I skip an iteration step in a while loop in Perl? - perl

In Perl I'm trying to achieve this:
while ($row = <$fh>){
if the row contains the character >:
#do something AND then skip to the next line
else:
#continue to parse normally and do other things

You can skip to the next iteration of a loop with the next built-in. Since you are reading line by line, that's all you need to do.
For checking if the character is present, use a regular expression. That's done with the m// operator and =~ in Perl.
while ($row = <$fh>) {
if ( $row =~ m/>/ ) {
# do stuff ...
next;
}
# no need for else
# continue and do other stuff ...
}

Try this way:
while ($row = <$fh>)
{
if($row =~ />/)
{
#do something AND then skip to the next line
next;
}
#continue to parse normally and do other things
}

Related

Perl, ne operator not working

Can someone please tell me why my ne operator isn't working in the below if statement? It was working but after saving my program, it has stopped working. Any help would be greatly appreciated, cheers.
$inFile = "animals.txt";
open (IN, $inFile) or
die "Can't find file: $inFile";
#animallist = (<IN>);
foreach $line (#animallist)
{
if ($line =~ $search)
{
print "$line <br> <br>";
}
}
if ($line ne $search)
{
print "$search isn't in the animal list";
}
print end_html;
You seem confused about what your program does, so I thought I'd just tell you.
$inFile = "animals.txt";
open (IN, $inFile) or die "Can't find file: $inFile";
#animallist = (<IN>);
# here you define a file name, open a file, and read all of the lines
# in the file into the array #animallist
foreach $line (#animallist) {
# here you iterate over all the lines, putting each line into $line
if ($line =~ $search) {
print "$line <br> <br>";
}
# here you perform the regex match: $line =~ /$search/ and if it
# succeeds, print $line
}
# here you end the loop
if ($line ne $search) {
print "$search isn't in the animal list";
}
# here you take the now uninitialized variable $line and try to match
# it against the as of yet undefined variable $search
# If both of these variables are undefined, and you are not using warnings
# it will simply return false (because "" ne "" is false)
# without warning about undefined variables in ne
You should be aware that even if your entire line was, for example, cat, you still could not compare it using ne to the string cat, because when read from a file, it has a trailing newline, so it is really cat\n. Unless you chomp it.
It seems redundant to tell you, but of course you cannot check if the file does not contain $search after you finished reading the file. You have to do that while reading the file.
Try using indentation so you can see when your blocks end at inappropriate places.
if ($line ne $search) isn't even within the foreach loop where you are populating and handling $line from the file. I suggest putting it within the block to at least get the functionality I assume you are looking for.
It's hard to know what you expect to happen as we don't know what $search contains. Let's assume it's the name of an animal that is in your file.
When you execute your code, $search contains, say, "frog" and $line contains undef (as $line only contains data within your foreach loop). Those values aren't the same so the ne returns true and the message is printed.
If you were to move that if statement into the foreach block then $line would have a value. But it still wouldn't match as lines are read from a file with the newline character still attached. And "Frog" is not the same as "Frog\n". You need to use chomp() to remove the newline from $line.
This is very similar to another recent question. Are you working on the same piece of homework?
I think there are several points here:
For the if ($line ne $search), the $line is out of the scope of the foreach where it was declared and since you are not using strict, you won't get error and this condition should always be true.
I assume that $line has a newline so when you match this $line with $search, the condition can be true although the strings are not equal.
Assuming that $line = "lion\n" and $search = "lion"
then if you do if ($line =~ $search), the condition will be be true, because "lion" is part of the "lion\n" string. This means that:
if ($line eq $search) is false
and if ($line ne $search) is true which I assume is your case.
you can use the chomp($line) function to remove newlines from the end of the string.
I hope this helps.

perl script miscounting because of empty lines

the below script is basically catching the second column and counting the values. The only minor issue I have is that the file has empty lines at the end (it's how the values are being exported) and because of these empty lines the script is miscounting. Any ideas please? Thanks.
my $sum_column_b = 0;
open my $file, "<", "file_to_count.txt" or die($!);
while( my $line = <$file>) {
$line =~ m/\s+(\d+)/; #regexpr to catch second column values
$sum_column_b += $1;
}
print $sum_column_b, "\n";
I think the main issue has been established, you are using $1 when it is not conditionally tied to the regex match, which causes you to add values when you should not. This is an alternative solution:
$sum_column_b += $1 if $line =~ m/\s+(\d+)/;
Typically, you should never use $1 unless you check that the regex you expect it to come from succeeded. Use either something like this:
if ($line =~ /(\d+)/) {
$sum += $1;
}
Or use direct assignment to a variable:
my ($num) = $line =~ /(\d+)/;
$sum += $num;
Note that you need to use list context by adding parentheses around the variable, or the regex will simply return 1 for success. Also note that, like Borodin says, this will give an undefined value when the match fails, and you must add code to check for that.
This can be handy when capturing several values:
my #nums = $line =~ /(\d+)/g;
The main problem is that if the regex does not match, then $1 will hold the value it received in the previous successful match. So every empty line will cause the previous line to be counted again.
An improvement would be:
my $sum_column_b = 0;
open my $file, "<", "file_to_count.txt" or die($!);
while( my $line = <$file>) {
next if $line =~ /^\s*$/; # skip "empty" lines
# ... maybe skip other known invalid lines
if ($line =~ m/\s+(\d+)/) { #regexpr to catch second column values
$sum_column_b += $1;
} else {
warn "problematic line '$line'\n"; # report invalid lines
}
}
print $sum_column_b, "\n";
The else-block is of course optional but can help noticing invalid data.
Try putting this line just after the while line:
next if ( $line =~ /^$/ );
Basically, loop around to the next line if the current line has no content.
#!/usr/bin/perl
use warnings;
use strict;
my $sum_column_b = 0;
open my $file, "<", "file_to_count.txt" or die($!);
while (my $line = <$file>) {
next if (m/^\s*$/); # next line if this is unsignificant
if ($line =~ m/\s+(\d+)/) {
$sum_column_b += $1;
}
}
print "$sum_column_b\n";

If condition not matching inside a foreach loop in perl

I'm trying to match a string with if statement inside a foreach loop but its not matching although i get the same string with printed before if statement inside foreach loop. Please help.
use Net::Telnet;
$ip='xx.xxx.xx.xx';
$ip_port='10002';
$port = new Net::Telnet->new( Host=>$ip,Port=>$ip_port,Dump_log=> "dump.log");
my #folder= $port->cmd("ls");
sleep(2);
$folders=#folder;
print "Number of folders are:$folders\n";
foreach my $folder(#folder)
{
print "Folder before if is:$folder\n";
if(($folder eq "acc") || ($folder eq "bda"))
{
# some code here.
}
}
Your strings probably contain white space. You can use something like chomp to remove it, or alternatively use regexs.
Try:
if ($folder =~ /^(acc|bda)/) {
# some code here
}

Perl IF statement not matching variables in REGEX

my $pointer = 0;
foreach (#new1)
{
my $test = $_;
foreach (#chk)
{
my $check = $_;
chomp $check;
delete($new1[$pointer]) if ($test =~ /^$check/i);
}
$pointer++;
}
The if statement never matches the fact that many entries in the #new1 array do contain $check at the start of the array element (88 at least).
I am not sure it is the nested loop that is causing the problem because if i try this it also fails to match:
foreach (#chk)
{
#final = (grep /^$_/, #new1);
}
#final is empty but I know at least 88 entires for $_ are in #new1.
I wrote this code on a machine running Windows ActivePerl 5.14.2 and the top code works. I then (using a copy of #new1) compare the two and remove any duplicates (also works on 5.14.2). I did try to negate the if match but that seemed to wipe out the #new1 array (so that I didn't need to do a hash compare).
When I try to run this code on a Linux RedHat box with Perl 5.8.0 it seems to struggle with the variable matching in the REGEX. If I hard code the REGEX with an example I know is in #new1 the match works and in the first code the entry is deleted (in the second one value is inserted in #final).
The #chk array is a listing file on the web server and the #new1 array is created by opening two log files on the web server and then pushing one into the other.
I had even gone to the trouble of printing out $test and $check in each loop iteration and manually checking to see if any of the the values did match and some of them do.
It has had me baffled for days now and I have had to throw the towel in and ask for help, any ideas?
As tested by user1568538, the solution was to replace
chomp $check;
with
$check =~ s/\r\n//g;
to remove Windows-style line endings from the variable.
Since chomp removes the contents of the input record separator $/ from the end of its argument, you could also change its value:
my $pointer = 0;
foreach (#new1)
{
my $test = $_;
foreach (#chk)
{
local $/="\r\n";
my $check = $_;
chomp $check;
delete($new1[$pointer]) if ($test =~ /^$_/i);
}
$pointer++;
}
However, since $/ also affects other operations (such as reading from a file handle), perhaps it is safest to avoid changing $/ unless you are sure if it is safe. Here I limit the change to the foreach loop where the chomp occurs.
No knowing what your input data looks like, using \Q might help:
if ($test =~ /^\Q$check/i);
See quotemeta.
It is not clear what you are trying to do. However, you may be trying to only get those elements for which there is no match or vice versa. Adapt the code below for your needs
#!/usr/bin/perl
use strict; use warnings;
my #item = qw(...); # your #new?
my #check = qw(...); # your #chk?
my #match;
my #nomatch;
ITEM:
foreach my $item (#item) {
CHECK:
foreach my $check (#check) {
# uncomment this if $check should not be interpreted as a pattern,
# but as literal characters:
# $item = '\Q' . $item;
if ($item =~ /^$check/) {
push #match, $item;
next ITEM; # there was a match, so this $item is burnt
# we don't need to test against other $checks.
}
}
# there was no match, so lets store it:
push #nomatch, $item.
}
print "matched $_\n" for #matched;
print "didn't match $_" for #nomatch;
Your code is somewhat difficult to read. Let me tell you what this
foreach (#chk) {
#final = (grep /^$_/, #new1);
}
does: It is roughly equivalent to
my #final = ();
foreach my $check (#chk) {
#final = grep /^$check/, #new1;
}
which is equivalent to
my #final = ();
foreach my $check (#chk) {
# #final = grep /^$check/, #new1;
#final = ();
foreach (#new) {
if (/^$check/) {
push #final, $_;
last;
}
}
}
So your #final array gets reset, possibly emptied.

Perl: Searching a file

I am creating a perl script that takes in the a file (example ./prog file)
I need to parse through the file and search for a string. This is what I thought would work, but it does not seem to work. The file is one work per line containing 50 lines
#array = < >;
print "Enter the word you what to match\n";
chomp($match = <STDIN>);
foreach $line (#array){
if($match eq $line){
print "The word is a match";
exit
}
}
You're chomping your user input, but not the lines from the file.
They can't match; one ends with \n the other does not. Getting rid of your chomp should solve the problem. (Or, adding a chomp($line) to your loop).
$match = <STDIN>;
or
foreach $line (#array){
chomp($line);
if($match eq $line){
print "The word is a match";
exit;
}
}
Edit in the hope that the OP notices his mistake from the comments below:
Changing eq to == doesn't "fix" anything; it breaks it. You need to use eq for string comparison. You need to do one of the above to fix your code.
$a = "foo\n";
$b = "bar";
print "yup\n" if ($a == $b);
Output:
yup