rein
rein

Reputation: 27

In perl, access one of the multiple lines which stored in single array element

I have this sample text which I would like to access one of the multiple lines from a single array element.

Student 1:
Math 83
Chemistry 60
Physics 75

Student 2:
Math 69
Chemistry 76
Physics 73
Art A-

My script for storing multiple lines to a single array element is as following:

use strict;
use warnings;

open my $fh, '<', 'Text1' or die "Could not open file to read:$!";
my $file = do { local $/; <$fh> };
my @a = split /\n\n/, $file;
close $fh;

print "First array element is $a[0]\n";
print "Second array element is $a[1]\n";

The output is

First array element is Student 1:
Math 83
Chemistry 60
Physics 75
Second array element is Student 2:
Math 69
Chemistry 76
Physics 73
Art A-

Is there a better way for me to access and grab one of the multiple lines in first or 2nd element of the array for further usage? i.e, I need Math score from each student. Here is what I come up so far, to join the first element of the array and split them again. Thanks in advance.

use strict;
use warnings;

open my $fh, '<', 'Text1' or die "Could not open file to read:$!";
my $file = do { local $/; <$fh> };
my @a = split /\n\n/, $file;
close $fh;

print "First array element is $a[0]\n";
print "Second array element is $a[1]\n";

my $str=join('',$a[0]);
my @score1 = split('\n',$str);
$str=join('',$a[1]);
my @score2 = split('\n',$str);

print "Student 1 : $score1[1]\n";
print "Student 2 : $score2[1]\n";

Upvotes: 0

Views: 219

Answers (2)

ysth
ysth

Reputation: 98388

Your read and split can be simplified to using readline's paragraph mode:

my @student = do { local $/ = ""; <$fh> };

I would be inclined to break each student up into a hash:

my @student = map {
    my ($student, $scores) = /\A(.*):\n(.*)/s;
    {
        'Name' => $student,
        split ' ', $scores
    }
} do { local $/ = ""; <$fh> };

for my $student_number (0..1) {
    print "Name: $student[$student_number]{Name}  Math Score: $student[$student_number]{Math}\n";
}

If there is no blank line between students:

my @student;
my $current_student;
while ( my $line = <$fh> ) {
    if ( my ($name) = $line =~ /^(.*):/ ) {
        $current_student = { 'Name' => $name };
        $line =~ s/^.*://;
        push @student, $current_student;
    }
    my @scores = split ' ', $line;
    while ( my ($subject, $score) = splice(@scores, 0, 2) ) {
        $current_student->{$subject} = $score;
    }
}

(A subject has to be on the same line as its score.)

Upvotes: 3

Chris Charley
Chris Charley

Reputation: 6573

Instead of 'slurping' the entire file, you could read in one 'paragraph' at a time by setting $/ to "". I would have used a hash instead of @score1 and @score2. Then you could address the sought math score by using Math as a key. It would look something like this-

#!/usr/bin/perl
use strict;
use warnings;

my @grades;

{
    local $/ = "";
    while (<DATA>) {
        push @grades, { split };
    }   
}

for my $href (@grades) {
    print "student: $href->{Student} Math: $href->{Math}\n";    
}

Output-

student: 1: Math: 83
student: 2: Math: 69

Upvotes: 3

Related Questions