【问题标题】:Reading in a CSV File in Perl在Perl中的CSV文件中读取
【发布时间】:2012-01-24 02:58:17
【问题描述】:

我以前在 Perl 中读取过文件,但当 CSV 文件在不同行上具有我需要的值时,我没有。我假设我必须创建一个与哈希键混合的数组,但我在这里不合群。

基本上,我的 CSV 文件包含以下列:branch, job, timePeriod, periodType, day1Value, day2Value, day3Value, day4Value, day4Value, day6Valueday7Value

day* 值分别表示一周中每一天的 periodType 值。

例如 -

东,庄家,9AM-12PM,加班,4.25,0,0,1.25,1.5,1.5,0,0
西,电工,12PM-5PM,常规,4.25,0,0,-1.25,-1.5,-1.5,0,0
北,看门人,5PM-12AM,方差,-4.25,0,0,-1.25,-1.5,-1.5,0,0
南,经理,12A-9AM,加班,77.75,14.75,10,10,10,10,10,

等等

我需要输出一个文件,将这些数据和键从分支、作业、时间段和日期中取出。我的输出将列出某一天的每个 periodType 值,而不是所有七个的一个 periodType 值。

例如-

South,Manager,12A-9AM,77.75,14.75,16

在上一行中,最后 3 个值代表三个 periodTypes(超时、常规和方差)day1Values

如您所见,我的问题是我不知道如何以一种允许我从不同行提取数据并成功输出的方式将数据加载到内存中。我以前只解析过单数行。

【问题讨论】:

    标签: perl csv


    【解决方案1】:

    除非您喜欢疼痛,否则请使用Text::CSV 及其亲戚Text::CSV_XSText::CSV_PP

    但是,这可能是这个问题中比较容易的部分。一旦您阅读并验证该行是完整的,您需要将相关信息添加到正确键入的哈希中。您可能还必须非常熟悉引用。

    您可以创建一个由分支键入的哈希%BranchData。该散列的每个元素都是对作业键入的散列的引用;并且其中的每个元素都是对由 timePeriod 键入的哈希的引用,并且其中的每个元素都将引用由天数键入的数组(使用索引 1..7;它稍微过度分配空间,但获得的机会正确的要大得多;不过不要惹$[!)。并且数组的每个元素都是对由三种句点类型键入的散列的引用。哎哟!

    如果一切正常,原型分配可能是这样的:

    $BranchData{$row{branch}}->{$row{job}}->{$row{period}}->[1]->{$row{p_type}} +=
        $row{day1};
    

    您将迭代元素 1..7 和 'day1' .. 'day7';需要对设计工作进行一些清理工作。

    你必须担心正确初始化东西(或者你可能没有——Perl 会为你做这件事)。我假设该行作为直接哈希(而不是哈希引用)返回,带有分支、作业、期间、期间类型(p_type)和每一天('day1',..'day7 ')。

    如果您提前知道需要哪一天,则可以避免累积所有天数,但它可能会使更通用的报告更易于阅读并一直累积所有数据,然后简单地让打印处理任何子集需要处理整个数据。


    这是一个非常有趣的问题,我将这段代码拼凑在一起。我怀疑它是否是最佳的,但它确实有效。

    #!/usr/bin/env perl
    #
    # SO 8570488
    
    use strict;
    use warnings;
    use Text::CSV;
    use Data::Dumper;
    use constant debug => 0;
    
    my $file = "input.csv";
    my $csv = Text::CSV->new({ binary => 1, eol => $/ })
                       or die "Cannot use CSV: ".Text::CSV->error_diag();
    my @headings = qw( branch job period p_type day1 day2 day3 day4 day5 day6 day7 );
    my @days     = qw( day0 day1 day2 day3 day4 day5 day6 day7 );
    my %BranchData;
    
    open my $in, '<', $file or die "Unable to open $file for reading ($!)";
    
    $csv->column_names(@headings);
    while (my $row = $csv->getline_hr($in))
    {
        print Dumper($row) if debug;
        my %r = %$row;  # Not for efficiency; for notational compactness
        $BranchData{$r{branch}} = { } if !defined $BranchData{$r{branch}};
        my $branch = $BranchData{$r{branch}};
        $branch->{$r{job}} = { } if !defined $branch->{$r{job}};
        my $job = $branch->{$r{job}};
        $job->{$r{period}} = [ ] if !defined $job->{$r{period}};
        my $period = $job->{$r{period}};
        for my $day (1..7)
        {
            # Assume that Overtime, Regular and Variance are the only types
            # Otherwise, you need yet another level of checking whether elements exist...
            $period->[$day] = { Overtime => 0, Regular => 0, Variance => 0} if !defined $period->[$day];
            $period->[$day]->{$r{p_type}} += $r{$days[$day]};
        }
    }
    
    print Dumper(\%BranchData);
    

    根据您的示例数据,输出如下:

    $VAR1 = {
        'West' => {
            'Electrician' => {
                '12PM-5PM' => [
                    undef,
                    {
                        'Regular'  => '4.25',
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => '-1.25',
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => '-1.5',
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => '-1.5',
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => 0
                    }
                ]
            }
        },
        'South' => {
            'Manager' => {
                '12A-9AM' => [
                    undef,
                    {
                        'Regular'  => 0,
                        'Overtime' => '77.75',
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => '14.75',
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 10,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 10,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 10,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 10,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 10,
                        'Variance' => 0
                    }
                ]
            }
        },
        'North' => {
            'Janitor' => {
                '5PM-12AM' => [
                    undef,
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => '-4.25'
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => '-1.25'
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => '-1.5'
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => '-1.5'
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => 0
                    }
                ]
            }
        },
        'East' => {
            'Banker' => {
                '9AM-12PM' => [
                    undef,
                    {
                        'Regular'  => 0,
                        'Overtime' => '4.25',
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => '1.25',
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => '1.5',
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => '1.5',
                        'Variance' => 0
                    },
                    {
                        'Regular'  => 0,
                        'Overtime' => 0,
                        'Variance' => 0
                    }
                ]
            }
        }
    };
    

    尽情享受吧!

    【讨论】:

    • 另一个值得考虑的模块是Text::CSV::Encoded,我用它来处理UTF-8。
    • 我相信这段代码能满足我的需要!我现在只需要将它以这种格式输出到另一个 CSV 文件:
      South,Manager,12A-9AM,77.75,14.75,16
      在上面的行中,最后 3 个值代表三个 periodTypes(加班,常规和方差)day1Values。
    【解决方案2】:

    我没有第一手经验,但您可以使用DBD::CSV,然后传递计算所需聚合所需的相对简​​单的 SQL 查询。

    但是,如果您坚持以艰难的方式进行,您可以循环并在以下哈希引用的哈希中收集您的数据:

    (
      "branch1,job1,timeperiod1"=>
        {
          "overtime"=>"overtimeday1value1",
          "regular"=>"regulartimeday1value1",
          "variance"=>"variancetimeday1value1"
        },
      "branch2,job2,timeperiod2"=>
        {
          "overtime"=>"overtimeday1value2",
          "regular"=>"regulartimeday1value2",
          "variance"=>"variancetimeday1value2"
        },
      #etc
    );
    

    然后相应地循环遍历这些键。然而,这种方法确实依赖于键的一致格式(例如"East,Banker,9AM-12PM""East, Banker, 9AM-12PM" 不同),因此您必须在生成上面的哈希时检查一致的格式(并强制执行)。

    【讨论】:

    • 对 - 我希望这样做是为了让值可以改变并且不会破坏程序 AKA 而不是硬代码。
    • @user1107055 - 我想你误解了我的意思:如果你想用艰难的方式来做,你必须逐行阅读你的文件并在上面创建散列引用的散列。因此,如果您在脚本中执行此操作,然后更改文件,然后再次运行脚本,您将获得不同的哈希(和不同的输出)。
    • 我用过DBD::CSV(实际上我主要使用一个简单的包装脚本csvsql,它允许我对CSV文件运行任意SQL查询)并且我可以确认它的用处。
    猜你喜欢
    • 1970-01-01
    • 2014-07-25
    • 1970-01-01
    • 2014-05-09
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    相关资源
    最近更新 更多