Perl Bug?

jonasbn on 2004-05-18T06:50:34

Yesterday my friend Allan mailed me a script, he could not understand did not work for me to have a look at.

After debugging using the Perl debugger, trying all kind of tricks and reading some documentation, I started to suspect a bug in Perl, when I later talked to him, he had made an even smaller version of the and he mailed it to me.

I ended up writing my own barebones version of the demo, which demonstrates the problem:

use strict;

my $str = q( J:somestring I:someother );

my $i = 0; my $err = "\t### ERR ###\n\tNo match\n"; my $success = "\tsuccess...\n";

foreach my $key ("J","I") { my $str2 = $str;

print "### RUN 1/".++$i." ###\nTesting \$str ... looking for $key\n"; if ($str =~ m/^$key:.+/gm) { print "$success\n"; } else { print "$err\n"; }

print "Testing \$str2 ... looking for $key\n"; if ($str2 =~ m/^$key:.+/gm) { print "$success\n"; } else { print "$err\n"; } }

foreach my $key ("I","J") { my $str2 = $str;

print "### RUN 2/".++$i." ###\nTesting \$str ... looking for $key\n"; if ($str =~ m/^$key:.+/gm) { print "$success\n"; } else { print "$err\n"; }

print "Testing \$str2 ... looking for $key\n"; if ($str2 =~ m/^$key:.+/gm) { print "$success\n"; } else { print "$err\n"; } }


The problem see to be the /g operator in the regular expressions when it is removed the thing works and as the demo shows, when 'J' comes before 'I' there is no problem.

This is the output:

### RUN 1/1 ###
Testing $str ... looking for J
        success...

Testing $str2 ... looking for J success...

### RUN 1/2 ### Testing $str ... looking for I success...

Testing $str2 ... looking for I success...

### RUN 2/3 ### Testing $str ... looking for I ### ERR ### No match

Testing $str2 ... looking for I success...

### RUN 2/4 ### Testing $str ... looking for J success...

Testing $str2 ... looking for J success...


If you alter the string so the part containing 'I' comes before the part that contains 'J', the problem is with 'J'.

I have searched for the string 'regex' at rt.perl.org only to be presented with 72 tickets. I have looking through some of these, but I have not yet found out whether this bug is already known.


You keep matching against $str

Whammo on 2004-05-18T12:13:49

You reset $str2 (in essence) on every loop, but you continually match against the same $str.

So if you unroll your loops, you get basically this.

Look for, at the beginning of the string, "J:", followed by a bunch of stuff that isn't a newline. (.+ with /m). Did you succeed? Remember this spot and proceed.

Look for, at the beginning of the string (where we left off), "I:", followed by a bunch of stuff that isn't a newline. Did you succeed? *Remember this spot and proceed*.

Look for, at the beginning of the string *where we left off*, "I:", followed by a bunch of stuff that isn't a newline. Did you succeed? No? Oh, well, reset.

Look for, at the beginning of the string, "J:", followed by a bunch of stuff that isn't a newline. Did you succeed?

From perlop

runrig on 2004-05-18T16:35:04

What Whammo said, and from perlop:
In scalar context, each execution of "m//g" finds the next match, returning true if it matches, and false if there is no further match. The position after the last match can be read or set using the pos() function; see the pos entry in the perlfunc manpage. A failed match normally resets the search position to the beginning of the string, but you can avoid that by adding the "/c" modifier (e.g. "m//gc"). Modifying the target string also resets the search position.