使用perl将分数与相应的人员相加

yxyvkwin  于 2022-12-19  发布在  Perl
关注(0)|答案(3)|浏览(121)

我有一个文件,其中包含许多人的数据和他们的分数。我试图采取每个人的平均分数。

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");
}

预期输出

Person 1 - (avg value)
Person 2 - (avg value)

我尝试了什么:

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(" ",$_);
        }
    }
}

在这一点之后,我无法将值存储在数组中以供进一步计算。

mwg9r5ms

mwg9r5ms1#

根据提供的数据结构,我们可以使用}作为记录分隔符,这简化了任务。
然后只需要从每个块中提取信息片段并进行简单的计算。

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");
}

产出

Person: 1 - (0.1744)
Person: 2 - (0.1784)
9ceoxa92

9ceoxa922#

代码的基本问题是,您每次遍历输入数据的一行,但您的代码假设解析代码所需的所有部分都存在于这一行中。
例如,这两个语句首先检查当前行上的文本Person,然后检查同一行上的文本字符串Scores *。

if (/Person/) {
        if (/Scores/../}/) {

解决这个问题的方法有很多,这里是其中之一。

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");
}

输出为

Person 1 - 0.1744
Person 2 - 0.1744
wgxvkvu9

wgxvkvu93#

perl -MList::Util=sum -0777 -wnE'
    %p = /Person\s+ (\S+) \s+ Scores \s+ (.+?)(?=Person|$)/gsx; 
    for (keys %p) { 
        @n = $p{$_} =~ /[0-9.]+/g; 
        say "Person $_: ", sum(@n)/@n 
    }
' person_score.txt

将整个文件读入字符串(-0777开关),然后在$_中可用。
在该字符串中,匹配Person并捕获以下标签,然后使用前瞻捕获Scores之后直到下一个Person(或字符串末尾)的所有标签;这些person-id + string-with-scores对被分配给散列中的键-值对。
然后,对于散列中的每个条目,从带有分数的字符串中提取数字(分数)并将其分配给一个数组,然后使用该数组来获得平均值。
或者作为一个程序放到一个文件里,确实比较好

use warnings;
use strict;
use feature 'say';
use List::Util qw(sum);

my $content = do { local $/; <> };

my %p = $content =~ /Person\s+ (\S+) \s+ Scores \s+ (.+?)(?=Person|$)/gsx; 

for my $id (keys %p) { 
    my @n = $p{$id} =~ /[0-9.]+/g; 
    say "Person $id: ", sum(@n)/@n 
}

这是作为program.pl filename运行的

相关问题