I am currently trying to pass a 32 by 48 matrix file to a multi-dimensional array in Perl. I am able to access all of the values, but I am having issues accessing a specific value.
Here is a link to the data set:
http://paste-it.net/public/x1d5301/
Here is what I have for code right now.
#!/usr/bin/perl
open FILE, "testset.txt" or die $!;
my #lines = <FILE>;
my $size = scalar #lines;
my #matrix = (1 .. 32);
my $i = 0;
my $j = 0;
my #micro;
foreach ($matrix)
{
foreach ($lines)
{
push #{$micro[$matrix]}, $lines;
}
}
It doesn't seem you understand that $matrix only indicates #matrix when it is immediately followed by an array indexer: [ $slot ]. Otherwise, $matrix is a completely different variable from #matrix (and both different from %matrix as well). See perldata.
#!/usr/bin/perl
use English;
Don't! use English--that way!
This brings in $MATCH, $PREMATCH, and $POSTMATCH and incurs the dreaded $&, $`, $' penalty. You should wait until you're using an English variable and then just import that.
open FILE, "testset.txt" or die $!;
Two things: 1) use lexical file handles, and 2) use the three-argument open.
my #lines = <FILE>;
As long as I'm picking: Don't slurp big files. (Not the case here, but it's a good warning.)
my $size = scalar #lines;
my #matrix = (1 .. 32);
my $i = 0;
my $j = 0;
my #micro;
I see we're at the "PROFIT!!" stage here...
foreach ($matrix) {
You don't have a variable $matrix; you have a variable #matrix.
foreach ($lines) {
The same thing is true with $lines.
push #{ $micro[$matrix]}, $lines;
}
}
Rewrite:
use strict;
use warnings;
use English qw<$OS_ERROR>; # $!
open( my $input, '<', 'testset.txt' ) or die $OS_ERROR;
# I'm going to assume space-delimited, since you don't show
my #matrix;
# while ( defined( $_ = <$input> ))...
while ( <$input> ) {
chomp; # strip off the record separator
# Load each slot of #matrix with a reference to an array filled with
# the line split by spaces.
push #matrix, [ split ]; # split = split( ' ', $_ )
}
If you are going to be doing quite a bit of math, you might consider PDL (the Perl Data Language). You can easily set up your matrix and before operations on it:
use 5.010;
use PDL;
use PDL::Matrix;
my #rows;
while( <DATA> ) {
chomp;
my #row = split /\s+/;
push #rows, \#row;
}
my $a = PDL::Matrix->pdl( \#rows );
say "Start ", $a;
$a->index2d( 1, 2 ) .= 999;
say "(1,2) to 999 ", $a;
$a++;
say "Increment all ", $a;
__DATA__
1 2 3
4 5 6
7 8 9
2 3 4
The output shows the matrix evolution:
Start
[
[1 2 3]
[4 5 6]
[7 8 9]
[2 3 4]
]
(1,2) to 999
[
[ 1 2 3]
[ 4 5 999]
[ 7 8 9]
[ 2 3 4]
]
Increment all
[
[ 2 3 4]
[ 5 6 1000]
[ 8 9 10]
[ 3 4 5]
]
There's quite a bit of power to run arbitrary and complex operations on every member of the matrix just like I added 1 to every member. You completely skip the looping acrobatics.
Not only that, PDL does a lot of special stuff to make math really fast and to have a low memory footprint. Some of the stuff you want to do may already be implemented.
You probably need to chomp the values:
chomp( my #lines = <FILE> );
To clarify a tangential point to Axeman's answer:
See perldoc -f split:
A split on /\s+/ is like a split(' ') except that any leading whitespace produces a null first field. A split with no arguments really does a split(' ', $_) internally.
#!/usr/bin/perl
use YAML;
$_ = "\t1 2\n3\f4\r5\n";
print Dump { 'split' => [ split ] },
{ "split ' '" => [ split ' ' ] },
{ 'split /\s+/' => [ split /\s+/ ] }
;
Output:
---
split:
- 1
- 2
- 3
- 4
- 5
---
split ' ':
- 1
- 2
- 3
- 4
- 5
---
split /\s+/:
- ''
- 1
- 2
- 3
- 4
- 5
I see the question is pretty old, but as the author has just edited the question, perhaps this is still of interest. Also the link to the data is dead, but since other answers use space as the separator, I will too.
This answer demonstrates Tie::Array::CSV which allows random access to a CSV (or other file parsable with Text::CSV).
#!/usr/bin/env perl
use strict;
use warnings;
use Tie::Array::CSV;
## put DATA into temporary file
## if not using DATA, put file name in $file
use File::Temp ();
my $file = File::Temp->new();
print $file <DATA>;
##
tie my #data, 'Tie::Array::CSV', $file, {
text_csv => {
sep_char => " ",
},
};
print $data[1][2];
__DATA__
1 2 3 4 5
6 7 8 9 1
2 3 4 5 6
Related
I have the following code which reads in a 6x6 array from STDIN and saves it as an array of anonymous arrays. I am trying to print out each element with $arr[i][j], but the code below isn't working. It just prints out the first element over and over. How am I not accessing the element correctly?
#!/user/bin/perl
my $arr_i = 0;
my #arr = ();
while ($arr_i < 6){
my $arr_temp = <STDIN>;
my #arr_t = split / /, $arr_temp;
chomp #arr_t;
push #arr,\#arr_t;
$arr_i++;
}
foreach my $i (0..5){
foreach my $j (0..5){
print $arr[i][j] . "\n";
}
}
i and j are not the same as the variables you declared in the foreach lines. Change:
print $arr[i][j] . "\n";
to:
print $arr[$i][$j] . "\n";
warnings alerted me to this issue. You should add these lines to all your Perl code:
use warnings;
use strict;
To demonstrate the Perlish mantra that there's "more than one way to do it":
use 5.10.0; # so can use "say"
use strict;
use warnings qw(all);
sub get_data {
my ($cols, $rows) = #_;
my ($line, #rows);
my $i;
for ($i = 1; $i <= $rows and $line = <DATA>; $i++) {
chomp $line;
my $cells = [ split ' ', $line ];
die "Row $i had ", scalar(#$cells), " instead of $cols" if #$cells != $cols;
push #rows, $cells;
}
die "Not enough rows, got ", $i - 1, "\n" if $i != $rows + 1;
\#rows;
}
sub print_data {
my ($cols, $rows, $data) = #_;
for (my $i = 0; $i < $rows; $i++) {
for (my $j = 0; $j < $cols; $j++) {
say $data->[$i][$j];
}
}
}
my $data = get_data(6, 6);
print_data(6, 6, $data);
__DATA__
1 2 3 4 5 6
a b c d e f
6 5 4 3 2 1
f e d c b a
A B C D E F
7 8 9 10 11 12
Explanation:
if we use say, that avoids unsightly print ..., "\n"
get_data is a function that can be called and/or reused, instead of just being part of the main script
get_data knows what data-shape it expects and throws an error if it doesn't get it
[ ... ] creates an anonymous array and returns a reference to it
get_data returns an array-reference so data isn't copied
print_data is a function too
both functions use a conventional for loop instead of making lists of numbers, which in Perl 5 needs to allocate memory
There is also a two-line version of the program (with surrounding bits, and test data):
use 5.10.0; # so can use "say"
my #lines = map { [ split ' ', <DATA> ] } (1..6);
map { say join ' ', map qq{"$_"}, #$_ } #lines;
__DATA__
1 2 3 4 5 6
a b c d e f
6 5 4 3 2 1
f e d c b a
A B C D E F
7 8 9 10 11 12
Explanation:
using map is the premier way to iterate over lists of things where you don't need to know how many you've seen (otherwise, a for loop is needed)
the adding of " around the cell contents is only to prove they've been processed. Otherwise the second line could just be: map { say join ' ', #$_ } #lines;
Is it possible to assign two variables the same data from an array in a Perl foreach loop?
I am using Perl 5, I think I came across something in Perl 6.
Something like this:
my $var1;
my $var2;
foreach $var1,$var2 (#array){...}
It's not in the Perl 5 core language, but List::Util has a pairs function which should be close enough (and a number of other pair... functions which may be more convenient, depending on what you're doing inside the loop):
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
use List::Util 'pairs';
my #list = qw(a 1 b 2 c 3);
for my $pair (pairs #list) {
my ($first, $second) = #$pair;
say "$first => $second";
}
Output:
a => 1
b => 2
c => 3
The easiest way to use this is with a while loop that calls splice on the first two elements of the array each time,
while (my($var1, $var2) = splice(#array, 0, 2)) {
...
}
However, unlike foreach, this continually does a double-shift on the original array, so when you’re done, the array is empty. Also, the variables assigned are copies, not aliases as with foreach.
If you don’t like that, you can use a C-style for loop:
for (my $i = 0; $i < #array; $i += 2) {
my($var1, $var2) = #array[$i, $i+1];
...
}
That leaves the array in place but does not allow you to update it the way foreach does. To do that, you need to address the array directly.
my #pairlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for (my $i = 0; $i < #pairlist; $i += 2) {
$pairlist[ $i + 0 ] x= 2;
$pairlist[ $i + 1 ] *= 2;
}
print "Array is #pairlist\n";
That prints out:
Array is feefee 2 fiefie 4 foefoe 6 fumfum 8
You can get those into aliased variables if you try hard enough, but it’s probably not worth it:
my #kvlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for (my $i = 0; $i < #kvlist; $i += 2) {
our ($key, $value);
local(*key, $value) = \#kvlist[ $i, $i + 1 ];
$key x= 2;
$value *= 2;
}
print "Array is #kvlist\n";
Which prints out the expected changed array:
Array is feefee 2 fiefie 4 foefoe 6 fumfum 8
Note that the pairs offered by the List::Pairwise module, which were but very recently added to the core List::Util module (and so you probably cannot use it), are still not giving you aliases:
use List::Util 1.29 qw(pairs);
my #pairlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for my $pref (pairs(#pairlist)) {
$pref->[0] x= 2;
$pref->[1] *= 2;
}
print "Array is #pairlist\n";
That prints out only:
Array is fee 1 fie 2 foe 3 fum 4
So it didn’t change the array at all. Oops. :(
Of course, if this were a real hash, you could double the values trivially:
for my $value (values %hash) { $value *= 2 }
The reasons that works is because those are aliases into the actual hash values.
You cannot change the keys, since they’re immutable. However, you can make a new hash that’s an updated copy of the old one easily enough:
my %old_hash = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
my %new_hash;
#new_hash{ map { $_ x 2 } keys %old_hash } =
map { $_ * 2 } values %old_hash;
print "Old hash is: ", join(" " => %old_hash), "\n";
print "New hash is: ", join(" " => %new_hash), "\n";
That outputs
Old hash is: foe 3 fee 1 fum 4 fie 2
New hash is: foefoe 6 fiefie 4 fumfum 8 feefee 2
A general algorithm for more than 2 variables:
while( #array ){
my $var1 = shift #array;
my $var2 = shift #array;
my $var3 = shift #array;
# other variables from #array
# do things with $var1, $var2, $var3, ...
}
PS: Using a working copy of the array to that it is preserved for use later:
if( my #working_copy = #array ){
while( #working_copy ){
my $var1 = shift #working_copy;
my $var2 = shift #working_copy;
my $var3 = shift #working_copy;
# other variables from #working_copy
# do things with $var1, $var2, $var3, ...
}
}
PPS: another way is to use indexing. Of course, that is a sure sign that the data structure is wrong. It should be an array of arrays (AoA) or an array of hashes (AoH). See perldoc perldsc and perldoc perllol.
my $i = 0;
while( $i < #array ){
my $var1 = $array[ $i++ ];
my $var2 = $array[ $i++ ];
my $var3 = $array[ $i++ ];
# other variables from #array
# do things with $var1, $var2, $var3, ...
}
PPPS: I've been asked to clarify why the data structure is wrong. It is a flatten set of tuples (aka records aka datasets). The tuples are recreated by counting of the number of data for each. But what is the reader constructing the set has a bug and doesn't always get the number right? If, for a missing value, it just skips adding anything? Then all the remaining tuples are shifted by one, causing the following tuples to be grouped incorrectly and therefore, invalid. That is why an AoA is better; only the tuple with the missing data would be invalid.
But an better structure would be an AoH. Each datum would access by a key. Then new or optional data can be added without breaking the code downstream.
While I'm at it, I'll add some code examples:
# example code for AoA
for my $tuple ( #aoa ){
my $var1 = $tuple->[0];
my $var2 = $tuple->[1];
my $var3 = $tuple->[2];
# etc
}
# example code for AoH
for my $tuple ( #aoh ){
my $var1 = $tuple->{keyname1};
my $var2 = $tuple->{key_name_2};
my $var3 = $tuple->{'key name with spaces'};
my $var4 = $tuple->{$key_name_in_scalar_variable};
# etc
}
Here is a module-less way to "loop" by an arbitrary value ($by) and output the resulting group of elements using an array slice:
#!perl -l
#array = "1".."6";
$by = 3; $by--;
for (my $i = 0 ; $i < #array ; $i += $by ) {
print "#array[$i..$i+$by]";
$i++ ;
}
As a one-liner to test (cut and paste to a Unix shell):
perl -E '#array = "1".."6"; $by = 3; $by--;
for (my $i = 0 ; $i < #array ; $i += $by ) {
say "#array[$i..$i+$by]"; $i++ }'
Output:
1 2 3
4 5 6
If you make $by = 2; it will print pairs of numbers. To get at specific elements of the resulting slice access it as an anonymous array: (e.g. [#array[$i..$i+$by]]->[1]).
See also:
How do I read two items at a time in a Perl foreach loop?
Perl way of iterating over 2 arrays in parallel
Some good responses there, including reference to natatime which is quite easy to use. It's easy to implement too - it is essentially a wrapper around the splice solutions mentioned in the responses here.
The following is not the nicest example, but I've been using autobox::Core and made an #array->natatime() "method" ;-) like this:
use autobox::Core ;
sub autobox::Core::ARRAY::natatime {
my ($self, $by) = #_;
my #copy = #$self ;
my #array ;
push #array, [splice (#copy, 0, $by) ] while #copy ;
if ( not defined wantarray ) {
print "#{ $_ } \n" for #array ;
}
return wantarray ? #array : \#array;
}
The #copy array is spliced destructively, but $self (which is how the #array in front of the autobox method -> arrow gets passed to the function) is still there. So I can do:
my #dozen = "1" .. "12" ; # cakes to eat
#dozen->natatime(4) ; # eat 4 at time
my $arr_ref = #dozen->natatime(4) ; # make a reference
say "Group 3: #{ $arr_ref->[2] }" ; # prints a group of elements
say scalar #dozen , " cakes left" ; # eat cake; still have it
Output:
1 2 3 4
5 6 7 8
9 10 11 12
Group 3: 9 10 11 12
12 cakes left
One other approach that also uses a CPAN module (I gave this answer elsewhere but it is worth repeating). This can also be done non-destructively, with Eric Strom's excellent List::Gen module:
perl -MList::Gen=":all" -E '#n = "1".."6"; say "#$_" for every 2 => #n'
1 2
3 4
5 6
Each group of elements you grab is returned in an anonymous array so the individual values are in: $_->[0] $_->[1] ... etc.
You mentioned Perl6, which handles multiple looping values nicely:
my #qarr = 1 .. 6;
my ($x, $y, $z) ;
for #qarr -> $x , $y , $z { say $x/$y ; say "z = " ~ $z }
Output:
0.5
z = 3
0.8
z = 6
For more on the Perl6 approach see: Looping for Fun and Profit from the 2009 Perl6 Advent Calendar, or the Blocks and Statements Synopsis for details. Perhaps Perl 5 will have a similar "loop by multliple values" construct one day - à la perl5i's foreach :-)
I'm Trying to print columns from data by using index key value in the outer part of a foreach loop.
my #col;
foreach(<DATA>){
#x = split(' ',$_);
#xz = ($x[0],$x[1],$x[2]) ;
#print "$x[0]\n"; This is working but i'm not expect this.
push(#col,#xz);
}
print "$col[0]\n";
__DATA__
7 2 3
3 2 8
6 7 2
I expect the output is
7 3 6
How can i do it?
Always use use strict; and use warnings;!!
You have a couple of issues:
push( #col, #xz );
In this case, you're losing your information in #xz array. After this loop, you end up with a single array that looks like this:
#col = ( 7, 2, 3, 3, 2, 8, 6, 7, 2);
So, when you print:
print "$col[0]\n";
You get that zeroth element: 7.
We can preserve the structure of the data by using a reference:
#! /usr/bin/env perl
#
use strict; # Lets you know when you misspell variable names
use warnings; # Warns of issues (using undefined variables
use feature qw(say);
use Data::Dumper;
my #columns;
for my $data ( <DATA> ) {
my #data_list = split /\s+/, $data;
push #columns, \#data_list;
}
say Dumper \#columns;
__DATA__
7 2 3
3 2 8
6 7 2
Here you see I've included Data::Dumper to print out the structure of #columns:
$VAR1 = [
[
'7',
'2',
'3'
],
[
'3',
'2',
'8'
],
[
'6',
'7',
'2'
]
];
As you can see, each entry in the #columns array is now another array. However, printing out $columns[0] array reference isn't going to print what you want. Instead, it's going to print the zeroth array reference: 7, 2, 3, and not the zeroth element of each array reference: 7, 3, 6.
To do that, we need a subroutine that will go through #columns and print out the the zeroth entry of each of the arrays. Here I'm creating a subroutine called fetch_index that will fetch the passed index of the passed array:
#! /usr/bin/env perl
#
use strict; # Lets you know when you misspell variable names
use warnings; # Warns of issues (using undefined variables
use feature qw(say);
use Data::Dumper;
my #columns;
for my $data ( <DATA> ) {
my #data_list = split /\s*/, $data;
push #columns, \#data_list;
}
say join ", ", fetch_index( 0, #columns );
sub fetch_index {
my $entry = shift; #Entry you want from all arrays
my #array = #_;
my #values;
for my $array_ref ( #array ) {
push #values, ${array_ref}->[$entry];
}
return #values;
}
__DATA__
7 2 3
3 2 8
6 7 2
The subroutine merely goes through each array reference I've stored in my array, and fetched the $entry value from that array reference. I push those into my #values array and return that.
my #col;
while (<DATA>) {
push #col, (split ' ')[0];
# push #col, /(\S+)/; # split alternative
}
print "#col\n";
__DATA__
7 2 3
3 2 8
6 7 2
output
7 3 6
Once you've absorbed the information about anonymous arrays and references in the other excellent posts here, you can start to have fun. e.g. you can often get a one liner approach to work:
perl -nE 'say [split]->[1] ' col_data.txt
would loop (-n creates an implicit while(){} loop) through the data in col_data.txt, split the topic variable ($_) creating a series of anonymous arrays from each row and then print the second element, or "column" for example.
You can use the autosplit command line option (-a) to split each row into an array called #F (mnemonic: "F" for "Field"). In later versions of perl, the -a implies the implicit while loop (-n):
perl -anE 'say $F[1] ' col_data.txt
would be the equivalent of the previous command - printing the second column:
output:
2
2
7
There is a famous and short perl workalike for cut that is a more featureful variaton on this theme, and there is this Perl Monks thread.
perl -a -F' ' -ne 'print "$F[0]\n";' data.txt
here you $F[0] is field you can change it accordingly you will get the expected output
You were pretty close I think. This is what I did (edited to reflect comments from #Borodin):
use strict;
use warnings;
sub getColumn {
my ($data, $col) = #_;
my #output = map $_->[$col], #{$data};
return #output;
}
my #data;
while (<DATA>){
push(#data, [split(' ',$_)]);
}
print join(' ', getColumn(\#data, 0), "\n");
print join(' ', getColumn(\#data, 1), "\n");
print join(' ', getColumn(\#data, 2), "\n");
__DATA__
7 2 3
3 2 8
6 7 2
That subroutine getColumn should let you retrieve any arbitrary column. When I ran it with your data I got this for output:
7 3 6
2 2 7
3 8 2
I expected the following to print in the order of the elements of #Data, but it's printing in the order of the elements of #Queries. Am I missing something? I also tried declaring the items to be printed after foreach(#data){... and then printing inside that loop, but still wrong order.
$datafile is a file with the following:
GR29929,JAMES^BOB
GR21122,HANK^REN
$queryfile is a file with the following:
(3123123212):# FD [GR21122]
line 2
line 3
line 4
(12): # FD [HANK^REN]
line 6
line 7
line 8
(13): # FD [Y]
-------------------------------
--------------------------------
(3123123212):# FD [GR29929]
line 2
line 3
line 4
(12): # FD [JAMES^BOB]
line 6
line 7
line 8
(13): # FD [Z]
The output file is:
GR21122,HANK^WREN,Y
GR29929,JAMES^BOB,Z
When I want:
GR29929,JAMES^BOB,Z
GR21122,HANK^WREN,Y
Code is:
open(DA, "<$datafile");
open(QR, "<$queryfile");
my #Data = <DA>;
my #Queries = <QR>;
foreach (#Data) {
my ( $acce, $namee ) = split( ',', $_ );
chomp $acce;
chomp $namee;
print "'$acce' and '$namee'\n";
for my $i ( 0 .. $#Queries ) {
my $Qacce = $Queries[$i];
my $Qname = $Queries[ $i + 4 ];
my $Gen = $Queries[ $i + 8 ];
if ( $Qacce =~ m/$acce/ and $Qname =~ m/$namee/ ) {
my ($acc) = $Qacce =~ /\[(.+?)\]/;
my ($gen) = $Gen =~ /\[(.+?)\]/;
$gen =~ s/\s+$//;
my ($name) = $Qname =~ /\[(.+?)\]/;
print GL "$i,$acc,$gen,$name\n";
}
}
}
The basic shell of your program prints what you ask for, but there is a lot missing. The refactoring below should do what you want.
You had a problem with the values of your $i index variable, so that the first time around the loop you were accessing #data elements [0, 4, 8], the second time [1, 5, 9] etc. It looks like the second loop execution should use elements [11, 15, 19] and so on. Please correct me if I'm wrong.
In addition you were using regular expressions to compare the keys in the two files, and you were finding nothing because the name values contain caret ^ characters which are special within regexes. Escaping the strings using \Q...\E fixed this.
Note that a better solution would use hashes to match keys across the two files, but without details on your file format - particularly queryfile - I have had to follow your own algorithm.
use strict;
use warnings;
use autodie;
my ($data_file, $query_file) = qw/ datafile.txt queryfile.txt /;
my #queries = do {
open my $query_fh, '<', $query_file;
<$query_fh>;
};
chomp #queries;
open my $data_fh, '<', $data_file;
while (<$data_fh>) {
chomp;
my ($acce, $namee) = split /,/;
for (my $i = 0; $i < #queries; $i += 11) {
my ($qacce, $qname, $qgen) = #queries[$i, $i+4, $i+8];
if ( $qacce =~ /\Q$acce\E/ and $qname =~ /\Q$namee\E/ ) {
my ($acc, $name, $gen) = map / \[ ( [^\[\]]+ ) \] /x, ($qacce, $qname, $qgen);
$gen =~ s/\s+\z//;
print "$acc,$name,$gen\n";
}
}
}
output
GR29929,JAMES^BOB,Z
GR21122,HANK^REN,Y
So I'm getting input from STDIN like:
1 2 3
4 5 6
7 6 3
4 3 2
2 3 5
2 5 1
Blank lines separate the matrices, so the above input should create two multi-dimensional arrays...I know how to create one (code below), but how do I create multiple ones depending on how many blank lines the user inputs?
I won't know how many arrays the user wants to create so how can I dynamically create arrays depending on the blank lines in the user input?
my #arrayrefs;
while(<>)
{
chomp;
my #data = split(/\s+/,$_);
push #arrayrefs, \#data;
}
for $ref (#arrayrefs){
print "[#$ref] \n";
}
With your data, I'd say using paragraph mode for the input stream would be a good idea. That is basically setting the input record separator $/ to "\n\n", but in this case we will use "", which is a bit more magical in that it is flexible with extra blank lines.
use strict;
use warnings;
use Data::Dumper;
sub parse_data {
my #matrix = map { [ split / / ] } split /\n/, shift;
return \#matrix;
}
my #array;
$/ = "";
while (<>) {
push #array, parse_data($_);
}
print Dumper \#array;
The map/split statement is not as complex as it looks. Reading from right to left:
shift an argument from the argument list #_
split that argument on newline
take each those (i.e. map them) split arguments and split them again on space, and put the result inside an anonymous array, using brackets [ ].
All done.
It won't win any Code Golf competition, but it does seem to work:
$ cat data
1 2 3
4 5 6
7 6 3
4 3 2
2 3 5
2 5 1
$ cat xx.pl
#!/usr/bin/env perl
use strict;
use warnings;
my #matrices;
my #matrix;
sub print_matrices()
{
print "Matrix dump\n";
foreach my $mref (#matrices)
{
foreach my $rref (#{$mref})
{
foreach my $num (#{$rref})
{
print " $num";
}
print "\n";
}
print "\n";
}
}
while(<>)
{
chomp;
if ($_ eq "")
{
my(#result) = #matrix;
push #matrices, \#result;
#matrix = ();
}
else
{
my #row = split(/\s+/,$_);
push #matrix, \#row;
}
}
# In case the last line of the file is not a blank line
if (scalar(#matrix) != 0)
{
my(#result) = #matrix;
push #matrices, \#result;
#matrix = ();
}
print_matrices();
$ perl xx.pl data
Matrix dump
1 2 3
4 5 6
7 6 3
4 3 2
2 3 5
2 5 1
$
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my #arrays = [];
while (<>) {
if (my #array = /(\d+)/g) {
push $arrays[$#arrays], \#array;
} else {
push #arrays, [];
}
}
$Data::Dumper::Indent = 0;
printf("%s\n", Dumper $arrays[0]);
printf("%s\n", Dumper $arrays[1]);
Output:
$VAR1 = [['1','2','3'],['4','5','6'],['7','6','3']];
$VAR1 = [['4','3','2'],['2','3','5'],['2','5','1']];