How to assign array variable as a hash? - arrays

I'm trying to print this hash. As key1 is the array[0], key2 is array[2] and $sum[0] is the value. But the has does not work. What I'm doing wrong?
#array=(10,45,20);
#sum=($array[0]+$array[1]+$array[2]);
%hash;
$hash{$array[0]}{$array[2]}=$sum[0]
At the end of the hashes I want to print 10 : 75 to the screen.

You've set
$hash{$array[0]}{$array[2]} = $sum[0]
which with the given values is
$hash{10}{20} = 75
If you want to print 10 : 75 from the hash then you need to write
printf "%d : %d\n",10, $hash{10}{20}
And while I'm sure you want something more general than that, you really haven't given enough information

From the description you gave to #ikegami «my program will accept ...» I created a file that would have the data:
data_1.txt:
john 10 45 20
alex 30 15 12
pete 23 45 10 21
will 06 56
bob 8 12 3
lazy
note that only the first two lines actually match the description, I'll come back to that later.
sum.pl:
use strict;
use warnings;
use List::Util 'sum';
# get the two filenames it should work with
#
my $filename_1 = shift;
my $filename_2 = shift;
# be sure we read a file for most modern systems, UTF-8
#
open( my $file1, '<:encoding(UTF-8)', $filename_1)
or die "Can't open file: $filename_1";
# create the (empty) data structure
#
my %sums_and_names;
#
# the % in perl means you are talking about a hash,
# use a sensible name instead of 'hash'
# read line by line
while ( my $line = <$file1> ) {
chomp $line; # get rid of the line endings
my ($name, #grades) = split ' ', $line;
#
# this is not strictly doing what you asked for, just more flexible
#
# split on ' ', a space character, splits on any asmount of (white) space
# your task said that there is one space.
# strictly, you could should split on / /, the regular expression
#
# the first part will go into the variable $name, the rest in array #grades
# strictly you have only three grades so the following would do
# my ($name, $grade_1, $grade_2, $grade_3) = split / /, $line;
my $sum = sum(#grades) // 'no grades';
#
# since we now can handle any number of grades, not just three, we could
# have no grades at all and thus result in `undef`
#
# using the function sum0 would return the value 0 instead
#
# you'll get away with the `undef` using in a hash assignment,
# it will turn it into an empty string `''`
=pod
$sums_and_names{foo}{bar} = [ 'baz', 'qux' ];
=cut
#
# here is where your task doesn't make sense
# i am guessing:
#
$sums_and_names{$sum}{$name} = \#grades;
#
# at least we have all the data from filename_1, and the sum of the grades
}
# please decide on what you want to print
use Data::Dumper;
print Dumper \%sums_and_names;
and running perl sum.pl data_1.txt data_2.txt will give you something like
output:
$VAR1 = {
'no grades' => {
'lazy' => []
},
'23' => {
'bob' => [
'8',
'12',
'3'
]
},
'57' => {
'alex' => [
'30',
'15',
'12'
]
},
'62' => {
'will' => [
'06',
'56'
]
},
'75' => {
'john' => [
'10',
'45',
'20'
]
},
'99' => {
'pete' => [
'23',
'45',
'10',
'21'
]
}
};
please note, strictly the block inside the while loop could had been written as:
chomp $line;
my ($name, $grade_1, $grade_2, $grade_3) = split / /, $line;
$sum = $grade_1 + $grade_2 + $grade_3;
$sums_and_names{$sum}{$name} = [ $grade_1, $grade_2, $grade_3 ];
but I quote from #Borodin:
And while I'm sure you want something more general than that, you really haven't given enough information

Always use use strict; use warnings qw( all );!!!
There's only one sum (at a time), so don't need an array.
There's no need for a hash of hash; a simple hash will do.
Fixed:
use strict;
use warnings qw( all );
use List::Util qw( sum );
my %hash;
while (...) {
my #nums = ...;
$hash{ $nums[0] } = sum(#nums);
}
for (sort { $a <=> $b } keys(%hash)) {
print("$_: $hash{$_}\n");
}

Related

Read space delimited text file into array of hashes [Perl]

I have text file that matches the following format:
1 4730 1031782 init
4 0 6 events
2190 450 0 top
21413 5928 1 sshd
22355 1970 2009 find
And I need to read it into a data structure in perl that will allow me to sort and print according to any of those columns.
From left to right the columns are process_id, memory_size, cpu_time and program_name.
How can I read a text file with formatting like that in a way that allows me to sort the data structure and print it according to the sort?
My attempt so far:
my %tasks;
sub open_file{
if (open (my $input, "task_file" || die "$!\n")){
print "Success!\n";
while( my $line = <$input> ) {
chomp($line);
($process_id, $memory_size, $cpu_time, $program_name) = split( /\s/, $line, 4);
$tasks{$process_id} = $process_id;
$tasks{$memory_size} = $memory_size;
$tasks{$cpu_time} = $cpu_time;
$tasks{$program_name} = $program_name;
print "$tasks{$process_id} $tasks{$memory_size} $tasks{$cpu_time} $tasks{$program_name}\n";
}
This does print the output correctly, however I can't figure out how to then sort my resulting %tasks hash by a specific column (i.e. process_id, or any other column) and print the whole data structure in a sorted format.
You're storing the values under keys that are equal to the values. Use Data::Dumper to inspect the structure:
use Data::Dumper;
# ...
print Dumper(\%tasks);
You can store the pids in a hash of hashes, using the value of each column as the inner key.
#!/usr/bin/perl
use strict;
use warnings;
use feature qw{ say };
my #COLUMNS = qw( memory cpu program );
my %sort_strings = ( program => sub { $a cmp $b } );
my (%process_details, %sort);
while (<DATA>) {
my ($process_id, $memory_size, $cpu_time, $program_name) = split;
$process_details{$process_id} = { memory => $memory_size,
cpu => $cpu_time,
program => $program_name };
undef $sort{memory}{$memory_size}{$process_id};
undef $sort{cpu}{$cpu_time}{$process_id};
undef $sort{program}{$program_name}{$process_id};
}
say 'By pid:';
say join ', ', $_, #{ $process_details{$_} }{#COLUMNS}
for sort { $a <=> $b } keys %process_details;
for my $column (#COLUMNS) {
say "\nBy $column:";
my $cmp = $sort_strings{$column} || sub { $a <=> $b };
for my $value (sort $cmp keys %{ $sort{$column} }
) {
my #pids = keys %{ $sort{$column}{$value} };
say join ', ', $_, #{ $process_details{$_} }{#COLUMNS}
for #pids;
}
}
__DATA__
1 4730 1031782 init
4 0 6 events
2190 450 0 top
21413 5928 1 sshd
22355 1970 2009 find
But if the data aren't really large and the sorting isn't time critical, just sorting the whole array of arrays by a given column is much easier to write and read:
#!/usr/bin/perl
use strict;
use feature qw{ say };
use warnings;
use enum qw( PID MEMORY CPU PROGRAM );
my #COLUMN_NAMES = qw( pid memory cpu program );
my %sort_strings = ((PROGRAM) => 1);
my #tasks;
push #tasks, [ split ] while <DATA>;
for my $column_index (0 .. $#COLUMN_NAMES) {
say "\nBy $COLUMN_NAMES[$column_index]:";
my $sort = $sort_strings{$column_index}
? sub { $a->[$column_index] cmp $b->[$column_index] }
: sub { $a->[$column_index] <=> $b->[$column_index] };
say "#$_" for sort $sort #tasks;
}
__DATA__
...
You need to install the enum distribution.
I can't figure out how to then sort my resulting %tasks hash by a specific column
You can't sort a hash. You need to convert each of your input rows in a hash (which you're doing successfully) and then store all of those hashes in an array. You can then print the contents of the array in a sorted order.
This seems to do what you want:
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my #cols = qw[process_id memory_size cpu_time program_name];
#ARGV or die "Usage: $0 [sort_order]\n";
my $sort = lc shift;
if (! grep { $_ eq $sort } #cols ) {
die "$sort is not a valid sort order.\n"
. "Valid sort orders are: ", join('/', #cols), "\n";
}
my #data;
while (<DATA>) {
chomp;
my %rec;
#rec{#cols} = split;
push #data, \%rec;
}
if ($sort eq $cols[-1]) {
# Do a string sort
for (sort { $a->{$sort} cmp $b->{$sort} } #data) {
say join ' ', #{$_}{#cols};
}
} else {
# Do a numeric sort
for (sort { $a->{$sort} <=> $b->{$sort} } #data) {
say join ' ', #{$_}{#cols};
}
}
__DATA__
1 4730 1031782 init
4 0 6 events
2190 450 0 top
21413 5928 1 sshd
22355 1970 2009 find
I've used the built-in DATA filehandle to make the code simpler. You would need to replace that with some code to read from an external file.
I've used a hash slice to simplify reading the data into a hash.
The column that you want to sort by is passed into the program as a command-line argument.
Note that you have to sort the last column (the program name) using string comparison and all other columns using numeric comparison.
This decides how to sort using the first argument the script receives.
#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
open my $fh, '<', 'task_file';
my #tasks;
my %sort_by = (
process_id=>0,
memory_size=>1,
cpu_time=>2,
program_name=>3
);
my $sort_by = defined $sort_by{defined $ARGV[0]?$ARGV[0]:0} ? $sort_by{$ARGV[0]} : 0;
while (<$fh>) {
push #tasks, [split /\s+/, $_];
}
#tasks = sort {
if ($b->[$sort_by] =~ /^[0-9]+$/ ) {
$b->[$sort_by] <=> $a->[$sort_by];
} else {
$a->[$sort_by] cmp $b->[$sort_by];
}
} #tasks;
for (#tasks) {
say join ' ', #{$_};
}

Adding values together from a hash

I'm trying to add keys together from a hash to get a total from the added values.
Here is what I have so far. Help is appreciated.
print "What is your name?\n";
$letters = <STDIN>;
%alphabet = {
a=>1, b=>2, c=>3, d=>4, e=>5, f=>6, g=>7, h=>8,
i=>9, j=>10, k=>11, l=>12, m=>13,n=>14, o=>15,
p=>16, q=>17, r=>18, s=>19, t=>20, u=>21, v=>22,
w=>23, x=>24, y=>25, z=>26
};
#characters = split('', $letters);
#$characters = keys (%alphabet);
foreach #$character {
$starting_total = 0;
$total = $starting_total + #$character - 10;
print "$total\n";
};
This program will do as you ask
Rather than using split, it applies a global regular expression that finds all of the alphabetic characters in the string. A call to lc makes each letter lower-case to match the hash keys
use strict;
use warnings 'all';
my %alphabet = (
a => 1, b => 2, c => 3, d => 4, e => 5, f => 6, g => 7, h => 8,
i => 9, j => 10, k => 11, l => 12, m => 13, n => 14, o => 15,
p => 16, q => 17, r => 18, s => 19, t => 20, u => 21, v => 22,
w => 23, x => 24, y => 25, z => 26
);
print 'What is your name? ';
my $name = <>;
my $total = 0;
while ( $name =~ /([a-z])/gi ) {
my $letter = $1;
my $n = $alphabet{lc $letter};
printf "%s %2d\n", $letter, $n;
$total += $n;
}
printf "Total %d\n", $total;
output
What is your name? Kokio
K 11
o 15
k 11
i 9
o 15
Total 61
Note that there is no need for a hash to calculate the index of a letter within the alphabet. You could do arithmetic on the code points of the letters, like this
my $n = 1 + ord(lc $letter) - (ord 'a');
or you could declare a constant string ALPHABET and then use index to find the position of each character within it
use constant ALPHABET => join "", 'a' .. 'z';
my $n = 1 + index ALPHABET, lc $letter;
These alternatives produce exactly the same result, as the solution above, and don't require the hash
I'm trying to add keys together from a hash to get a total from the added values.
I don't think you are. The keys in your hash are letters. You can't (sensibly) add letters together. I think you're trying to add together the values from a hash which match a list of keys.
Accuracy and precision are important traits in a programmer. If you can't describe your problem accurately and precisely, then you have little chance of solving it.
Your code doesn't even compile. Let's take a look at it.
# You should always start your Perl programs with "use strict"
# and "use warnings".
print "What is your name?\n";
# When you "use strict" you will need to declare all of your variables
# using "my". So "my $letters = <STDIN>"
$letters = <STDIN>;
# Similarly, "my %alphabet = ..."
# But there are far better ways to set up this hash, as we'll see
# later.
# Also (as Borodin points out in a comment) you have initialised this
# hash incorrectly. A hash should be initialised with a list:
# %alphabet = (a => 1, ...);
# Note the round parentheses indicating a list.
# You have initialised your hash with a single-element list containing
# a hash reference - braces { ... } are the anonymous hash constructor
# and they return a reference to the new hash.
# This is an error that would have been picked up by "use warnings".
%alphabet = {
a=>1, b=>2, c=>3, d=>4, e=>5, f=>6, g=>7, h=>8,
i=>9, j=>10, k=>11, l=>12, m=>13,n=>14, o=>15,
p=>16, q=>17, r=>18, s=>19, t=>20, u=>21, v=>22,
w=>23, x=>24, y=>25, z=>26
};
# "my #characters ..."
#characters = split('', $letters);
# But you're also using an array reference called $characters.
# That's bound to confuse you at some point in the future
#$characters = keys (%alphabet);
# This is the bit that doesn't compile. It should be
# "foreach (#character)". But that's also not right as it uses
# an array called #character, and you don't have an array called
# #character (you have an array called #characters). "use strict"
# will catch errors like this.
# Also, each time round this loop, one of the elements from #character
# will be put into $_. But you don't use $_ in your code at all.
foreach #$character {
# Do you really want to set this to 0 each time?
$starting_total = 0;
# #$character is the number of elements in the array referenced
# by $character. Which is zero as you don't have an array
# reference called $character. I assume you meant #$characters,
# but that is always going to be 26 - which doesn't seem useful.
# And why subtract 10?
$total = $starting_total + #$character - 10;
print "$total\n";
}
Your description of the problem is incredibly vague, but looking at your code (and guessing a lot) I think what you're trying to do is this:
Get a name for the user
Split the name into individual letters
Encode each letter into a number (a=1, b=2, ..., z=26)
Sum the letters in the name
Here's how I would do that.
#/usr/bin/perl
use strict;
use warnings;
# We use modern Perl, specifically say()
use 5.010;
print 'What is your name? ';
chomp(my $name = <STDIN>);
my %letters;
#letters{'a' .. 'z'} = (1 .. 26);
my $total;
foreach (split //, $name) {
$_ = lc $_; # force lower case
next unless exists $letters{$_}; # ignore non-letters
$total += $letters{$_};
}
say "$name is $total";
I don't know what you exactly want. I added the script which gives the addition of the character position
print "Enter your name: ";
chomp (my $name = <STDIN>);
my #arc = split('',$name);
my $total;
my $lc_offset = ord("a") - 1;
foreach (#arc)
{
$total+=(ord(lc($_))) - $lc_offset;
}
print $total;
No need to store the position of the alphabets in hashes. Becuase perl has inbuilt function ord. so the small letters are starts at 97.
It's quite unclear from your question, so I will guess you want to get the numeric sum of all letters in a word.
#!/usr/bin/perl
use strict;
use warnings;
use constant ORD_LC_OFFSET => ord('a') - 1;
print "What is your name?\n";
chomp (my $name = <STDIN>);
my $sum = 0;
$sum += ord( lc($_) ) - ORD_LC_OFFSET for grep { m/[a-zA-Z]/ } split '', $name;
print "$sum\n";
We split the name to the characters and grep only the letter characters. Then we convert each character to the index of the letter (ord does the magic and converts the letter to it's ASCII value). Now we add that to $sum.

How to build a Perl multidimensional array or hash?

I have a set of CSV values like this:
device name, CPU value, frequency of CPU value, CPU in percentage
For example
router1,5,10,4
router1,5,1,5
router2,5,10,4
router2,5,2,5
router3,4,5,6
router3,7,6,5
I need to form a data structure like this:
array = {
router1 => [5,10,4],[5,1,5],
router2 => [5,10,4],[5,2,5],
router3 => [4,5,6],[7,6,5]
}
I need help in forming this data structure in Perl.
I have tried visualizing how to do this but am unable to do so. I would appreciate any help on this.
The end goal for me is to convert this into a JSON object.
This should get you started. It uses the DATA file handle so that I could embed the data in the program itself. I have used to_json from the JSON module to format the hash as JSON data. The statement $_ += 0 for #values converts the contents of #values from string to to numeric, to avoid quotation marks in the resultant JSON data.
use strict;
use warnings;
use JSON;
my %data;
while (<DATA>) {
chomp;
my ($device, #values) = split /,/;
$_ += 0 for #values;
push #{ $data{$device} }, \#values;
}
print to_json(\%data, { pretty => 1, canonical => 1 });
__DATA__
router1,5,10,4
router1,5,1,5
router2,5,10,4
router2,5,2,5
router3,4,5,6
router3,7,6,5
output
{
"router1" : [
[
5,
10,
4
],
[
5,
1,
5
]
],
"router2" : [
[
5,
10,
4
],
[
5,
2,
5
]
],
"router3" : [
[
4,
5,
6
],
[
7,
6,
5
]
]
}
Here is a simple solution which prints desired JSON object.
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
my %hash;
while (my $line = <DATA>) {
chomp $line;
my ($device, #cpu_values) = split(/,/, $line);
my $cpu_token = join(",", #cpu_values);
$hash{$device} .= '[' . $cpu_token . '], ';
}
my #devices = keys %hash;
print "array = { \n";
foreach (sort #devices) {
print "$_ => [$hash{$_}]\n";
}
print "}\n";
__DATA__
router1,5,10,4
router1,5,1,5
router2,5,10,4
router2,5,2,5
router3,4,5,6
router3,7,6,5
In Perl you need to use references in the way of anonymous arrays and hashes to make multidimensional arrays, arrays of arrays, hashes containing hashes and anywhere in between. perlreftut should cover how to accomplish what you are trying to do. Here is an example I wrote the other day that could help explain as well:
print "\nFun with multidimensional arrays\n";
my #myMultiArray = ([1,2,3],[1,2,3],[1,2,3]);
for my $a (#myMultiArray){
for my $b (#{$a}){
print "$b\n";
}
}
print "\nFun with multidimensional arrays containing hashes\nwhich contains an anonymous array\n";
my #myArrayFullOfHashes = (
{'this-key'=>'this-value','that-key'=>'that-value'},
{'this-array'=>[1,2,3], 'this-sub' => sub {return 'hi'}},
);
for my $a (#myArrayFullOfHashes){
for my $b (keys %{$a}){
if (ref $a->{$b} eq 'ARRAY'){
for my $c (#{$a->{$b}}){
print "$b.$c => $c\n";
}
} elsif ($a->{$b} =~ /^CODE/){
print "$b => ". $a->{$b}() . "\n";
} else {
print "$b => $a->{$b}\n";
}
}
}

How to create hash with duplicate keys

Now i am modifying the code a little
I am using the code for creating hash haivng duplicate keys. Its giving the syntax error.
use strict;
use warnings;
my $s = "12 A P1
23 B P5
24 C P2
15 D P1
06 E P5";
my $hash;
my #a = split(/\n/, $s);
foreach (#a)
{
my $c = (split)[2];
my $d = (split)[1];
my $e = (split)[0];
push(#{$hash->{$c}}, $d);
}
print Dumper($hash );
i am getting the output as
$VAR1 = {
'P5' => [
'B',
'E'
],
'P2' => [
'C'
],
'P1' => [
'A',
'D'
]
};
But i want the output like
$VAR1 = {
'P5' => {
'E' => '06',
'B' => '23'
},
'P2' => {
'C' => '24'
},
'P1' => {
'A' => '12',
'D' => '15'
}
};
How to do that
You hash declaration is incorrect, it should be:
my %hash = ();
or simply:
my %hash;
Then the rest of your code is both too complex and incorrect.
foreach (#a) {
my ($k, $v) = (split);
push #{$hash{$k}}, $v;
}
should be enough. See Autovivification for why this works.
With your code, the first time you see a key, you set $hash{$k} to be a scalar. You can't then push things to that key - it needs to be an array to begin with.
The if (-e $hash{$c}) test is wrong. -e is a file existence test. If you want to know if a hash key exists, use:
if (exists $hash{$c}) { ... }
And print %hash; won't do what you expect (and print %{$hash}; is invalid). You'll get a prettier display if you do:
use Data::Dumper;
print Dumper(\%hash);
(Great debugging too, this Data::Dumper.)
Perl is telling you exactly what is wrong. You have used the strict pragma, so using the %hash variable without declaring it is a syntax error. While the string %hash does not appear in your code, the string $hash{...} does, on each of the problem lines. This is the syntax to access an element of the %hash, which is why strict is complaining.
You have declared the variable $hash, so accessing an element of the contained hash reference is written $$hash{...} or $hash->{...}. Fix the problem lines to access the correct variable and the code will compile.
%hash is a hash, and $hash is a scalar (a hash reference, like \%hash ), they are two different variables
To refer to $hash, to refer to the hash whose reference is stored in the scalar variable $hash, you either have to use $hash->{$c} or $$hash{$c}
See References quick reference
update:
#!/usr/bin/perl --
use strict; use warnings;
use Data::Dumper;
my $s = "P1 26
P5 23
P2 24
P1 15
P5 06 ";
my $hash = {};
for my $line ( split /[\r\n]+/, $s ) {
my( $c, $d ) = split ' ', $line;
push #{ $hash->{$c} }, $d;
}
print Dumper( $hash );
__END__
$VAR1 = {
'P5' => [
'23',
'06'
],
'P2' => [
'24'
],
'P1' => [
'26',
'15'
]
};
See the working code, the fixed errors (comments in the code), and the resulting output:
use strict;
use warnings;
my $s = "P1 26
P5 23
P2 24
P1 15
P5 06 ";
my %hash; #my $hash ={};
#my $arr = [];
my #a = split(/\n/, $s);
foreach (#a)
{
my $d = (split)[1];
my $c = (split)[0];
push(#{$hash{$c}}, $d); #if ...
}
while (my ($key, $value) = each(%hash)) #print %{$hash};
{
print "$key #{$value}\n";
}
#Output:
#P5 23 06
#P2 24
#P1 26 15
(Strange. Out of all the answers posted so far, none has actually answered the question...)
The code below produces the result asked for. The fundamental bit which seems to be missing from the original code is the two-level hash.
As an aside, there seems to be no reason for the outer hash to be a hashref and not a hash, so I made it a hash. Also you can pick out the split into variables in one line.
use strict;
use warnings;
use Data::Dumper;
my $s = "12 A P1
23 B P5
24 C P2
15 D P1
06 E P5";
my %hash;
my #a = split(/\n/, $s);
foreach (#a)
{
my ($e, $d, $c) = (split);
$hash{$c}{$d} = $e;
}
print Dumper(\%hash);

How can I store captures from a Perl regular expression into separate variables?

I have a regex:
/abc(def)ghi(jkl)mno(pqr)/igs
How would I capture the results of each parentheses into 3 different variables, one for each parentheses? Right now I using one array to capture all the results, they come out sequential but then I have to parse them and the list could be huge.
#results = ($string =~ /abc(def)ghi(jkl)mno(pqr)/igs);
Your question is a bit ambiguous to me, but I think you want to do something like this:
my (#first, #second, #third);
while( my ($first, $second, $third) = $string =~ /abc(def)ghi(jkl)mno(pqr)/igs) {
push #first, $first;
push #second, $second;
push #third, $third;
}
Starting with 5.10, you can use named capture buffers as well:
#!/usr/bin/perl
use strict; use warnings;
my %data;
my $s = 'abcdefghijklmnopqr';
if ($s =~ /abc (?<first>def) ghi (?<second>jkl) mno (?<third>pqr)/x ) {
push #{ $data{$_} }, $+{$_} for keys %+;
}
use Data::Dumper;
print Dumper \%data;
Output:
$VAR1 = {
'first' => [
'def'
],
'second' => [
'jkl'
],
'third' => [
'pqr'
]
};
For earlier versions, you can use the following which avoids having to add a line for each captured buffer:
#!/usr/bin/perl
use strict; use warnings;
my $s = 'abcdefghijklmnopqr';
my #arrays = \ my(#first, #second, #third);
if (my #captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
push #{ $arrays[$_] }, $captured[$_] for 0 .. $#arrays;
}
use Data::Dumper;
print Dumper #arrays;
Output:
$VAR1 = [
'def'
];
$VAR2 = [
'jkl'
];
$VAR3 = [
'pqr'
];
But I like keeping related data in a single data structure, so it is best to go back to using a hash. This does require an auxiliary array, however:
my %data;
my #keys = qw( first second third );
if (my #captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
push #{ $data{$keys[$_]} }, $captured[$_] for 0 .. $#keys;
}
Or, if the names of the variables really are first, second etc, or if the names of the buffers don't matter but only order does, you can use:
my #data;
if ( my #captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
push #{ $data[$_] }, $captured[$_] for 0 .. $#captured;
}
An alternate way of doing it would look like ghostdog74's answer, but using an array that stores hash references:
my #results;
while( $string =~ /abc(def)ghi(jkl)mno(pqr)/igs) {
my ($key1, $key2, $key3) = ($1, $2, $3);
push #results, {
key1 => $key1,
key2 => $key2,
key3 => $key3,
};
}
# do something with it
foreach my $result (#results) {
print "$result->{key1}, $result->{key2}, $result->{key3}\n";
}
with the main advantage here of using a single data structure, AND having a nice readable loop.
#OP, when parenthesis are captured, you can use the variables $1,$2....these are backreferences
$string="zzzabcdefghijklmnopqrsssszzzabcdefghijklmnopqrssss";
while ($string =~ /abc(def)ghi(jkl)mno(pqr)/isg) {
print "$1 $2 $3\n";
}
output
$ perl perl.pl
def jkl pqr
def jkl pqr
You could have three different regex's each focusing on specific groups. Obviously, you would like to just assign different groups to different arrays in the regex, but I think your only option is to split the regex up.
You can write a regex containing named capture groups. You do this with the ?<myvar> construct at the beginning of the capture group:
/(?<myvar>[0-9]+)/
You may then refer to those named capture groups using a $+{myvar} form.
Here is a contrived example:
perl -ne '/^systemd-(?<myvar>[^:]+)/ && { print $+{myvar} . "\n"}' /etc/passwd
Given a typical password file, it pulls out the systemd users and returns the names less the systemd prefix. It uses a capture group named myvar. This is just an example thrown together to illustrate the use of capture group variables.

Resources