Diamond
Diamond

Reputation: 157

Reading numbers from a file to variables (Perl)

I've been trying to write a program to read columns of text-formatted numbers into Perl variables.

Basically, I have a file with descriptions and numbers:

ref   5.25676      0.526231      6.325135
ref   1.76234     12.62341       9.1612345

etc.

I'd like to put the numbers into variables with different names, e.g.

ref_1_x=5.25676
ref_1_y=0.526231

etc.

Here's what I've got so far:

print "Loading file ...";
open (FILE, "somefile.txt");
@text=<FILE>;
close FILE;
print "Done!\n";
my $count=0;
foreach $line (@text){
    @coord[$count]=split(/ +/, $line);
}

I'm trying to compare the positions written in the file to each other, so will need another loop after this.

Upvotes: 1

Views: 7581

Answers (2)

DVK
DVK

Reputation: 129383

Sorry, you weren't terribly clear on what you're trying to do and what "ref" refers to. If I misunderstood your problem please commend and clarify.


First of all, I would strongly recommend against using variable names to structure data (e.g. using $ref_1_x to store x coordinate for the first row with label "ref").

If you want to store x, y and z coordinates, you can do so as an array of 3 elements, pretty much like you did - the only difference is that you want to store an array reference (you can't store an array as a value in another array in Perl):

my ($first_column, @data) = split(/ +/, $line); # Remove first "ref" column
@coordinates[$count++] = \@data; # Store the reference to coordinate array

Then, to access the x coordinate for row 2, you do:

$coordinates[1]->[0]; # index 1 for row 2; then sub-index 0 for x coordinate.

If you insist on storing the 3 coordinates in named data structure, because sub-index 0 for x coordinate looks less readable - which is a valid concern in general but not really an issue with 3 columns - use a hash instead of array:

my ($first_column, @data) = split(/ +/, $line); # Remove first "ref" column
@coordinates[$count++] = { x => $data[0], y => $data[1], z => $data[2] };
# curly braces - {} - to store hash reference again

Then, to access the x coordinate for row 2, you do:

$coordinates[1]->{x}; # index 1 for row 2

Now, if you ALSO want to store the rows that have a first column value "ref" in a separate "ref"-labelled data structure, you can do that by wrapping the original @coordinates array into being a value in a hash with a key of "ref".

my ($label, @data) = split(/ +/, $line); # Save first "ref" label
$coordinates{$label} ||= []; # Assign an empty array ref 
                          #if we did not create the array for a given label yet.
push @{ $coordinates{$label} }, { x => $data[0], y => $data[1], z => $data[2] };
# Since we don't want to bother counting per individual label, 
# Simply push the coordinate hash at the end of appropriate array.
# Since coordinate array is stored as an array reference, 
# we must dereference for push() to work using @{ MY_ARRAY_REF } syntax

Then, to access the x coordinate for row 2 for label "ref", you do:

$label = "ref";
$coordinates{$label}->[1]->{x}; # index 1 for row 2 for $label

Also, your original example code has a couple of outdated idioms that you may want to write in a better style (use 3-argument form of open(), check for errors on IO operations like open(); use of lexical filehandles; storing entire file in a big array instead of reading line by line).

Here's a slightly modified version:

use strict;
my %coordinates;
print "Loading file ...";
open (my $file, "<", "somefile.txt") || die "Can't read file somefile.txt: $!";
while (<$file>) {
    chomp;
    my ($label, @data) = split(/ +/); # Splitting $_ where while puts next line
    $coordinates{$label} ||= []; # Assign empty array ref if not yet assigned
    push @{ $coordinates{$label} }
       , { x => $data[0], y => $data[1], z => $data[2] };
}
close($file);
print "Done!\n";

It is not clear what you want to compare to what, so can't advise on that without further clarifications.

Upvotes: 4

Wes Hardaker
Wes Hardaker

Reputation: 22252

The problem is you likely need a double-array (or hash or ...). Instead of this:

@coord[$count]=split(/ +/, $line);

Use:

@coord[$count++]=[split(/ +/, $line)];

Which puts the entire results of the split into a sub array. Thus,

print $coord[0][1];

should output "5.25676".

Upvotes: 0

Related Questions