【问题标题】:Appending values to Hash if key is same in Perl如果 Perl 中的键相同,则将值附加到 Hash
【发布时间】:2017-04-19 21:10:10
【问题描述】:

问题是在每个新行读取一个具有值的文件。文件内容看起来像

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

【问题讨论】:

    标签: perl hash


    【解决方案1】:

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

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

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


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

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

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

    .* 是贪心的,会尽可能匹配(包括一些带逗号的内容)。因此,如果任何行包含超过六个逗号分隔的项目,则第一个匹配组中将包含多个项目。这在您的实际数据中可能不是问题,但它不是编写代码的理想方式。表达方式过于模棱两可。

    最好这样写:

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

    这只会匹配正好有六个项目的行。

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

    【讨论】:

    • 提供的正则表达式产生完全相同的结果并且没有工作
    【解决方案2】:

    这比你做的要简单得多。您可以将每一行拆分为字段并使用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
    

    【讨论】:

    • 这对我有用。但是我确实必须删除严格。
    • @KaushikBose:您必须永远删除use strict。它可以告诉您代码中的错误,您应该修复这些错误,而不是删除它给您的建议。您可能只需要在适当的范围内声明一个变量。
    猜你喜欢
    • 2021-12-07
    • 2017-07-04
    • 2020-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-21
    • 2023-02-07
    相关资源
    最近更新 更多