I have a file which contains data with many persons & their scores. i am trying to take avg of scores of each person.
Person 1
Scores (\
"0.06, 0.01, 0.07, 0.07, 0.75", \
"0.05, 0.08, 0.01, 0.09, 0.08", \
"0.10, 0.10, 0.11, 0.12, 0.10", \
"0.18, 0.19, 0.20, 0.20, 0.19", \
"0.31, 0.32, 0.32, 0.33, 0.32");
}
Person 2
Scores (\
"0.06, 0.01, 0.07, 0.07, 0.75", \
"0.05, 0.08, 0.01, 0.09, 0.08", \
"0.10, 0.10, 0.11, 0.12, 0.10", \
"0.18, 0.19, 0.20, 0.20, 0.19", \
"0.31, 0.32, 0.32, 0.33, 0.32");
}
Expected Output
Person 1 - (avg value)
Person 2 - (avg value)
What i tried:
open($in, “<file.txt>”)
or die;
while(<$in>) {
if (/Person/) {
if (/Scores/../}/) {
$_ =~ s/,//g;
$_ =~ s/\\//g; # removing all unwanted characters to take avg of numbers
$_ =~ s/"//g;
$_ =~ s/values//g;
$_ =~ s/\(//g;
$_ =~ s/\)//g;
$_ =~ s/;//g;
$_ =~ s/}/ /g;
@a1 = split(" ",$_);
}
}
}
After this point i am not able to store the values in array for further computation.
CodePudding user response:
The fundamental problem with your code is you are walking a line at a time through your input data, but you code is making the assumption that all the parts you need to parse the code are present in that one line.
For example these two statements are checking first for the literal Person
on the current line, and then checking for the literal string Scores
on the same line. That will never match -- the two literal strings are on different lines
if (/Person/) {
if (/Scores/../}/) {
There are lots of approaches to this problem, here is one of them.
use strict;
use warnings ;
use List::Util qw(sum);
# read the complete file into $data
my $data ;
{
local $/;
$data = <DATA>;
}
# repeatedly match each Person/Scores section
while ($data =~ /Person\s (\S )\s Scores\s \((. ?)\)/smg)
{
my $person = $1;
my $scores = $2;
# now split $scores into the individual values - store in @scores
my @scores;
while ($scores =~ /(\d \.\d )/smg)
{
push @scores, $1
}
# @scores now holds the individual values.
# Can work out the average from them
my $average = sum(@scores) / scalar @scores;
print "Person $person - $average\n";
}
__DATA__
Person 1
Scores (
"0.06, 0.01, 0.07, 0.07, 0.75",
"0.05, 0.08, 0.01, 0.09, 0.08",
"0.10, 0.10, 0.11, 0.12, 0.10",
"0.18, 0.19, 0.20, 0.20, 0.19",
"0.31, 0.32, 0.32, 0.33, 0.32");
}
Person 2
Scores (
"0.06, 0.01, 0.07, 0.07, 0.75",
"0.05, 0.08, 0.01, 0.09, 0.08",
"0.10, 0.10, 0.11, 0.12, 0.10",
"0.18, 0.19, 0.20, 0.20, 0.19",
"0.31, 0.32, 0.32, 0.33, 0.32");
}
output is
Person 1 - 0.1744
Person 2 - 0.1744
CodePudding user response:
Based on provided data structure we can utilize }
as record separator, it simplifies the task.
Then it is just a matter of extracting pieces of information from each block and make simple computation.
use strict;
use warnings;
use feature 'say';
$/ = '}';
while( my $record = <DATA> ) {
next unless $record =~ /Person (\d)/;
my $data;
$data->{person} = $1;
$data->{scores}->@* = $record =~ /(\d\.\d{2})/gsm;
$data->{sum} = $_ for @{$data->{scores}};
$data->{count} = scalar $data->{scores}->@*;
$data->{average} = $data->{sum}/$data->{count};
say "Person: $data->{person} - ($data->{average})";
}
exit 0;
__DATA__
Person 1
Scores (\
"0.06, 0.01, 0.07, 0.07, 0.75", \
"0.05, 0.08, 0.01, 0.09, 0.08", \
"0.10, 0.10, 0.11, 0.12, 0.10", \
"0.18, 0.19, 0.20, 0.20, 0.19", \
"0.31, 0.32, 0.32, 0.33, 0.32");
}
Person 2
Scores (\
"0.06, 0.01, 0.07, 0.07, 0.75", \
"0.05, 0.08, 0.01, 0.09, 0.08", \
"0.10, 0.10, 0.11, 0.12, 0.20", \
"0.18, 0.19, 0.20, 0.20, 0.19", \
"0.31, 0.32, 0.32, 0.33, 0.32");
}
Output
Person: 1 - (0.1744)
Person: 2 - (0.1784)