This would be a *nearl* correct solution which picks up only the "runs" of length 3:

Code

my @runs=$letters =~ m( (.) # \1 some character (?!\1) # negative look-ahead: next character must be different ((.)\3\3) # Run of 3 identical characters (?!\3) # negative look-ahead: next character must be different )gx;

However, this solution has a drawback: It does not retrieve a 3-character-run at the *beginning* of the string.

A workaround would be to put at the string a character which we know won't occur within the string, for example

Code

my @runs=(chr(0).$letters) =~ m(...)gx;

This works, but is a hack.

Maybe someone can improve my solution by making it work without this hack?

Capturing using the list mode won't work as it will return all the subgroups, not just the 3 character string.

However, you can enhance your regex by simply adding the option for the beginning of the string.

Code

use strict; use warnings;

my $letters = "aaabbbbbcccdeeeeeefffgggg";

while ($letters =~ m{ (?: (.) # \1 some character (?!\1) # negative look-ahead: next character must be different | ^ ) ((.)\3\3) # Run of 3 identical characters (?!\3) # negative look-ahead: next character must be different }gx) { print $2, "\n"; }

As I said before though, this is extremely messy, and it's much cleaner to just rely on greedy matching and do length filtering after the fact. Yes, the above works, but it requires decoding to understand.

Capturing using the list mode won't work as it will return all the subgroups, not just the 3 character string.

Hmmm... I tried my example, and for the data supplied, it *only* captured 3-character-subgroups, as required. This is not related to list mode, but to negative look-ahead. I just don't like it because of the "char(0)"-hack.

Yes, you can get around the hack by using the enhanced regex that I built from yours.

However, list mode will still return all the capturing subgroups, not just $2.

Code

my $letters = "aabbbbbcccdeeeeeefffgggg";

my @runs = $letters =~ m( (.) # \1 some character (?!\1) # negative look-ahead: next character must be different ((.)\3\3) # Run of 3 identical characters (?!\3) # negative look-ahead: next character must be different )gx;

print join(',', @runs), "\n";

=prints b,ccc,c,e,fff,f =cut

It's easily solved though, just put it in a while loop so that $2 can be pulled explicitly.