Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite while-loop in perl

Tags:

regex

perl

Is there a way to do this without getting an infinite loop?

while((my $var) = $string =~ /regexline(.+?)end/g) {
    print $var;
}

This results in an infinite loop, probably because the assigning of a var directly from a regex inside the while returns "true" every time?

I know I can do this:

while($string =~ /regexline(.+?)end/g) {
     my $var = $1;      
     print $var;
}

But I was hoping I could save a line. Is there a regex modifier I can use or something like that?

(Also, what is this notation/trick actually called, if I want to search for it:

(my $var) = $string =~ /regex/;

Thanks!!

like image 575
Jeff Avatar asked Sep 10 '11 19:09

Jeff


3 Answers

In scalar context, a regular expression with the /g modifier will act like an iterator and return a false value when there are no more matches:

print "$1\n" while "abacadae" =~ /(a\w)/g;     # produces "ab","ac","ad","ae"

With the assignment inside the while expression, you are evaluating your regular expression in list context. Now your regular expression doesn't act like an iterator anymore, it just returns the list of matches. If the list is not empty, it evaluates to a true value:

print "$1\n" while () = "abacadae" =~ /(a\w)/g;   # infinite "ae"

To fix this, you can take the assignment out of the while statement and use the builtin $1 variable to make the assignment inside the loop?

while ($string =~ /regexline(.+?)end/g) {
    my $var = $1;
    print $var;
}
like image 151
mob Avatar answered Sep 28 '22 08:09

mob


Is there a way to do this without getting an infinite loop?

Yes. Use a foreach() instead of a while() loop:

foreach my $var ($string =~ /regexline(.+?)end/g) {

what is this notation/trick actually called, if I want to search for it

It is called a match in list context. It is described in "perldoc perlop":

The g modifier specifies global pattern matching--that is, matching as many times as possible within the string. How it behaves depends on the context. In list context ...

like image 27
tadmc Avatar answered Sep 28 '22 07:09

tadmc


The Perl regular expressions tutorial says:

In scalar context, successive invocations against a string will have //g jump from match to match, keeping track of position in the string as it goes along.

But:

In list context, //g returns a list of matched groupings, or if there are no groupings, a list of matches to the whole regexp.

That is to say, in list context //g returns an array of all your captured matches at once (of which you subsequently discard all but the first), and then does that all over again every time your loop executes (i.e. forever).

So you can't use the list context assignment in a loop condition, because it doesn't do what you want.

If you insist on using the list context, you could do this instead:

foreach my $var ($string =~ /regexline(.+?)end/g) {
    print $var;
}
like image 22
mercator Avatar answered Sep 28 '22 07:09

mercator