3

I tried to store the first group in @a1 and second group in @a2

my $string = "abc123def456abc321def654";
my (@a1,@a2) = $string =~m/abc(\d+)def(\d+)/g;
$, = "\n";
print @a1; 

Result is 123 456 321 654 and then @a2 is empty. But i want the result is @a1 = 123 321; @a2 = 456 654 For this result i tried hash , for to store the keys are first grouping and the values are second grouping but the problem is duplicates are removed. Any possible way for to do it with a single regex matching ?

I tried eval group within pattern matching. I get the correct result from @a1 but @a2 going wrong. And don't know what is the problem?

my (@a1, @a2);
$string =~m/abc(\d+) (?{push @a1, $1}) def(\d+)(?{push @a2,$2 }) \G/x;

@a1 result is

1234
321
666

@a2 result is

4567
456
45
4
654
65
6
777
77
7
3
  • Why are you trying to coerce this into a single regex? Commented Apr 4, 2016 at 10:32
  • @sorbrique I had one interview call he said it is possible with a single regex. So im trying it for long time with a eval group yet I could not get result Commented Apr 4, 2016 at 12:38
  • @mkHun Since you seem to strictly need it all inside a regex I added that to my answer. Commented Apr 4, 2016 at 18:19

4 Answers 4

2

You could do:

use List::Util 'pairs';
my @a12 = pairs $string =~ /abc(\d+)def(\d+)/g;
my @a1 = map $_->[0], @a12;
my @a2 = map $_->[1], @a12;

or

use List::MoreUtils 'part';
my $i = 0;
my @part = part { $i++ % 2 } $string =~ /abc(\d+)def(\d+)/g;
my @a1 = @{ $part[0] };
my @a2 = @{ $part[1] };

Or just skip setting @a1 and @a2 and use the data in @a12 or @part.

Sign up to request clarification or add additional context in comments.

Comments

1

This is what you're after;

#!/usr/bin/env perl
use v5.12;

my $string = "abc123def456abc321def654";
my (@a1, @a2);
while ($string =~ /abc(\d+)def(\d+)/g) {
    push @a1, $1;
    push @a2, $2;
}
$, = "\n" ;
say @a1;
say @a2;

# Output
# 123
# 321
# 456
# 654

As a general rule, try not to presume matches are successful. Rather than assign directly to variables, perform a boolean test ('if' or 'while) on the result of the match first, and then do the assignment.

5 Comments

I think this answer might benefit from an explanation of why the numbers are all ending up in the first array.
If you remove $, = "\n" and replace say @a1; say @a2 with say join(", ", @a1); say join(", ", @a2); you will see that all the numbers are not in the first array.
@Marty I tried eval group with \G, any possible for to do it.
@Sobrique Any possible way for to do it with eval grouping?
I can't help any further except to say that \d+ will give you 456 for $2 the first time, then the regex will try again and succeed with $2 being 45, then succeed again with just 4 - because they all satisfy \d+ - but how you got '7's in there when they're not in the original string, I have no idea. The use of anchors would stop that repeated matching.
1

Update   Added a version which does it all in regex, based on requirement clarifications.


Build a hash: Key is the pattern (abc, def, etc), value is the array reference with its matches.

 my %h;
() = $string =~ /([a-z]+)(\d+)(?{push @{$h{$1}}, $2;})/g;

The () is needed to impose the list context, since otherwise only the first match happens, with /g only making it jump from match to match. See perlrequick. Note that running code in the matching part is an experimental feature. Instead, you can evaluate code in the substitution part.

my %h;
$string =~ s/([a-z]+)(\d+)/{push @{$h{$1}}, $2}/eg;

This is of course destructive to the original string.

The resulting hash has keys which are the patterns (abc, def, ...), with key values being array-ref with all matches for that key. You can have any number of pattern-digits pairs, not only two. See below.

Both of these variants print the same as below.


Posted initially

This assumes a pattern (your abc or def) followed by digits. Here the pattern used is [a-zA-Z], per your examples. You can have any number of such pairs, not only two.

use warnings;
use strict;

my $string = "abc123def456abc321def654";

my %h; 
my $patt = qr/[a-zA-Z]/;  # for example

push @{$h{$1}}, $2  while ($string =~ /($patt+)(\d+)/g);

print "$_ => @{$h{$_}}\n" for keys %h;

Prints

def => 456 654 
abc => 123 321 

With more than two pattern-digits pairs (added ghi111 and ghi222),

my $string = "abc123def456abc321ghi111def654ghi222";
# exactly the same code ...

Prints

def => 456 654 
abc => 123 321 
ghi => 111 222

2 Comments

Thank you for your helpful answer. I learn new things from you. But i found the answer for my question with atomic group. :)
I have no idea what the -1 is for. The code runs and prints as shown.
1

I found the answer for my question. I used atomic group and capture the result. Backtracking is problem of my second case so i used atomic groups, help to avoid the backtracking.

$string =~m/abc(\d+)(?{push @a1, $1;})def(?>(\d+)) (?{ push @a2,$2; })\G/x;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.