in reply to Re^2: Regex troubles...
in thread Regex troubles...

You can also store the matching parts in an array in a (?{}) expression:
#!/usr/bin/perl use warnings; use strict; use feature qw{ say }; for my $string (qw( ab1b2b3d ab1b2x )) { my @two; my @matches = $string =~ /(a) (b.(?{ push @two, $2 if defined $2 }))+ (d)/x; say for @matches, '---', @two, '======'; }

($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

Replies are listed 'Best First'.
Re^4: Regex troubles...
by AnomalousMonk (Archbishop) on Apr 23, 2016 at 19:24 UTC

    That doesn't seem to yield any output that I can understand. Why three 'b1' 'b2' 'b3' from ab1b2b3b4d and nothing from any of the others?

    c:\@Work\Perl\monks>perl -e "use warnings; use strict; use feature qw{ say }; ;; for my $string (qw(ab1b2b3b4d ab5b6b7d ab8b9d abxd ad)) { my @two; my @matches = $string =~ /(a) (b.(?{ push @two, $2 if defined $2 }))+ (d)/x; say for @matches, '---', @two, '======'; } " a b4 d --- b1 b2 b3 ====== a b7 d --- ====== a b9 d --- ====== a bx d --- ====== --- ======

    Update: "This is perl 5, version 14, subversion 4 (v5.14.4) built for MSWin32-x86-multi-thread"


    Give a man a fish:  <%-{-{-{-<

      Weird, I'm getting a different output:
      a b4 d --- b1 b2 b3 ====== a b7 d --- b5 b6 ====== a b9 d --- b8 ====== a bx d --- ====== --- ======

      This is perl 5, version 20, subversion 1 (v5.20.1) built for x86_64-linux-thread-multi

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

        Ok, it looks like the for-loop business is just some bug that got fixed between (my) version 5.14.4.1 and (your) version 5.20.

        As to the difference between
            (b.(?{ push @two, $2 if defined $2 }))+
        and
            (?: (b.) (?{ push @two, $2 }))+

        It seems to me that in the first expression, the location of the  (?{ ... }) sub-expression within the second capture group means that on the first occasion that  b. can match within string, the capture group has not actually "closed" yet, and so  $2 is undefined. On each subsequent occasion, the previous capture to  $2 is what gets pushed to the array. So, the  @two array is always "one behind" in its contents if the first (undefined) value of  $2 is excluded.

        In the second expression, the second capture group is always fully closed at the moment it is pushed to the  @two array, so no weirdness.


        Give a man a fish:  <%-{-{-{-<

        Update: While the  (?: (b.) (?{ push @two, $2 }))+ approach discussed in this reply can certainly work, I think I would prefer the two-step approach outlined in choroba's reply above for its greater readability and maintainability.

        If I take the for-loop out of the equation, I get something closer to what I expect (still Strawberry 5.14.4.1):

        c:\@Work\Perl\monks>perl -wMstrict -le "my $string = 'ab1b2b3b4d'; my @two; my @matches = $string =~ /(a) (b.(?{ push @two, $2 if defined $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; ;; $string = 'ab5b6b7d'; @two = (); @matches = $string =~ /(a) (b.(?{ push @two, $2 if defined $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; ;; $string = 'ab8b9d'; @two = (); @matches = $string =~ /(a) (b.(?{ push @two, $2 if defined $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; ;; $string = 'abxd'; @two = (); @matches = $string =~ /(a) (b.(?{ push @two, $2 if defined $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; ;; $string = 'ad'; @two = (); @matches = $string =~ /(a) (b.(?{ push @two, $2 if defined $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; " 'ab1b2b3b4d' -> all:(a b4 d) -> 2:(b1 b2 b3) 'ab5b6b7d' -> all:(a b7 d) -> 2:(b5 b6) 'ab8b9d' -> all:(a b9 d) -> 2:(b8) 'abxd' -> all:(a bx d) -> 2:() 'ad' -> all:() -> 2:()
        And if I re-arrange the capturing, I get exactly what I expect:
        c:\@Work\Perl\monks>perl -wMstrict -le "my $string = 'ab1b2b3b4d'; my @two; my @matches = $string =~ /(a) (?: (b.) (?{ push @two, $2 if defined $2 }) +)+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; ;; $string = 'ab5b6b7d'; @two = (); @matches = $string =~ /(a) (?: (b.) (?{ push @two, $2 if defined $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; ;; $string = 'ab8b9d'; @two = (); @matches = $string =~ /(a) (?: (b.) (?{ push @two, $2 if defined $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; ;; $string = 'abxd'; @two = (); @matches = $string =~ /(a) (?: (b.) (?{ push @two, $2 if defined $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; ;; $string = 'ad'; @two = (); @matches = $string =~ /(a) (?: (b.) (?{ push @two, $2 if defined $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; " 'ab1b2b3b4d' -> all:(a b4 d) -> 2:(b1 b2 b3 b4) 'ab5b6b7d' -> all:(a b7 d) -> 2:(b5 b6 b7) 'ab8b9d' -> all:(a b9 d) -> 2:(b8 b9) 'abxd' -> all:(a bx d) -> 2:(bx) 'ad' -> all:() -> 2:()

        Update: The expression
            (?: (b.) (?{ push @two, $2 if defined $2 }))+
        works just as well without the statement modifier:
            (?: (b.) (?{ push @two, $2 }))+
        I don't understand the use of the statement modifier: Could  $2 ever be undefined in this situation?


        Give a man a fish:  <%-{-{-{-<

      This one works for me.

      #!/usr/bin/perl use warnings; use strict; for my $string (qw(ab1b2b3b4d ab5b6b7d ab8b9d abxd ad)) { my @two; my @matches = $string =~ /(a) (?:(b.)(?{ push @two, $2 }))+ (d)/x; @matches and splice @matches, 1, 1, @two; print "@matches\n"; }

        If I put the for-loop back into the unrolled loop code, I start to see weird stuff again (still Strawberry 5.14.4.1):

        c:\@Work\Perl\monks>perl -wMstrict -le "for my $string (qw(ab1b2b3b4d ab5b6b7d ab8b9d abxd ad)) { my @two; my @matches = $string =~ /(a) (?: (b.) (?{ push @two, $2 }))+ (d)/x; print qq{'$string' -> all:(@matches) -> 2:(@two)}; } " 'ab1b2b3b4d' -> all:(a b4 d) -> 2:(b1 b2 b3 b4) 'ab5b6b7d' -> all:(a b7 d) -> 2:() 'ab8b9d' -> all:(a b9 d) -> 2:() 'abxd' -> all:(a bx d) -> 2:() 'ad' -> all:() -> 2:()
        What Perl version are you using?


        Give a man a fish:  <%-{-{-{-<