如果Perl中的键相同,则将值附加到Hash

时间:2017-04-19 11:35:27

标签: perl hash

问题是在每个新行读取一个带有值的文件。文件内容类似于

3ssdwyeim3,3ssdwyeic9,2017-03-16,09:10:35.372,0.476,EndInbound
3ssdwyeim3,3ssdwyfyyn,2017-03-16,09:10:35.369,0.421,EndOutbound
3ssdwyfxc0,3ssdwyfxfi,2017-03-16,09:10:35.456,0.509,EndInbound
3ssdwyfxc0,3ssdwyhg0v,2017-03-16,09:10:35.453,0.436,EndOutbound

使用第一个逗号之前的字符串作为键,并在最后一个和最后一个逗号之间输入字符串值

即。对于第一行3ssdwyeim3成为关键和0.476值。

现在,如果密钥存在,我们循环遍历每一行,我们必须连接用逗号分隔的值。

因此,对于下一个新行,因为密钥已存在,密钥仍为3ssdwyeim3但值已更新为0.476,0.421.

最后,我们必须在文件中打印键和值。

我已经编写了一个代码来实现相同的功能,如下所示。

sub findbreakdown {
    my ( $out ) = @_;

    my %timeLogger;

    open READ, "out.txt" or die "Cannot open out.txt for read :$!";

    open OUTBD, ">$out\_breakdown.csv" or die "Cannot open $out\_breakdown.csv for write :$!";

    while ( <READ> ) {

        if ( /(.*),.*,.*,.*,(.*),.*/ ) {

            $btxnId = $1;
            $time   = $2;

            if ( !$timeLogger{$btxnId} ) {
                $timeLogger{$btxnId} = $time;
            }
            else {
                $previousValue       = $timeLogger{$btxnId};
                $newValue            = join ",", $previousValue, $time;
                $timeLogger{$btxnId} = $newValue;
            }
        }

        foreach ( sort keys %timeLogger ) {
            print OUTBD "$_ ,$timeLogger{$_}\n";
        }
    }

    close OUTBD;
    close READ;
}

然而有些东西出了问题,它就像这样打印

3ssdwyeim3,0.476
3ssdwyeim3,0.476,0.421
3ssdwyeim3,0.476,0.421
3ssdwyfxc0,0.509
3ssdwyeim3,0.476,0.421
3ssdwyfxc0,0.509,0.436
3ssdwyeim3,0.476,0.421
3ssdwyfxc0,0.509,0.436

预期是:

3ssdwyeim3,0.476,0.421
3ssdwyfxc0,0.509,0.436

2 个答案:

答案 0 :(得分:3)

您的程序运行正常,但在处理每一行后,您将打印整个哈希的当前状态。

因此,您在拥有完整的值集之前打印哈希键,并且您有许多重复的行。

如果将打印的foreach循环移动到程序的末尾(或者只是使用调试器来检查变量),您会发现散列的最终状态正是您所期望的。

编辑:我以前认为问题出在下面,但这是因为我误读了你问题中的示例数据。

这个正则表达式并不理想:

if (/(.*),.*,.*,.*,(.*),.*/) {

.*贪婪并且会尽可能匹配(包括一些逗号内容)。因此,如果任何行包含六个以上以逗号分隔的项目,则第一个匹配组中将包含多个项目。这可能不是您实际数据中的问题,但它不是编写代码的理想方式。表达式比必要的更模糊。

最好这样写:

if (/^([^,]*),[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$/) {

只能匹配六个项目的行。

或者考虑在输入行上使用split,这将是一个更清洁的解决方案。

答案 1 :(得分:1)

这比你做的简单得多。您可以将每一行拆分为字段,然后使用push将值添加到与该键对应的列表中

我相信您可以将其修改为从外部文件而不是DATA文件句柄中读取吗?

use strict;
use warnings 'all';

my %data;

while ( <DATA> ) {
    my @fields = split /,/;
    push @{ $data{$fields[0]} }, $fields[-2];
}

for my $key ( sort keys %data ) {
    print join(',', $key, @{ $data{$key} }), "\n";
}

__DATA__
3ssdwyeim3,3ssdwyeic9,2017-03-16,09:10:35.372,0.476,EndInbound
3ssdwyeim3,3ssdwyfyyn,2017-03-16,09:10:35.369,0.421,EndOutbound
3ssdwyfxc0,3ssdwyfxfi,2017-03-16,09:10:35.456,0.509,EndInbound
3ssdwyfxc0,3ssdwyhg0v,2017-03-16,09:10:35.453,0.436,EndOutbound

输出

3ssdwyeim3,0.476,0.421
3ssdwyfxc0,0.509,0.436