根据开始和结束日期解析逗号分隔文件

时间:2011-03-20 22:56:06

标签: perl parsing csv text

我是perl的新手,所以如果我的问题很简单,请接受我的道歉。我有一个非常大的文件,其数据如下所示:

Date, Time, Data1, Data2, Data3  
1/4/1999,9:31:00 AM,blah, blah, blah  
1/4/1999,9:32:00 AM,blah, blah, blah  
1/4/1999,9:33:00 AM,blah, blah, blah  

我有一个名为'cities.txt'的文件,其中有一个位于不同行的城市列表,行末有逗号。

Boston,  
Atlanta,  
Seattle,  

每个城市在同一目录中都有自己的文件,该文件具有以下命名约定'Boston 1 Minute Moisture Data.txt'。我想首先阅读'cities.txt'文件,并为该文件中出现的每个城市找到相关的湿度数据文件,并提取两组日期之间的所有数据(行)(START和END日期)并将其保存到另一个文件。日期位于第一列。

我已阅读以下帖子中的评论,但我仍然非常困惑。

How do I efficiently parse a CSV file in Perl?

我在网上使用一些例子写了一个简单的脚本。首先,我只是想看看我是否正确使用该模块。所以我想做的就是让解析器解析字段并计算特定列的总和。

#!/usr/bin/perl  
use strict;  
use warnings;  

use Text::CSV_XS;  
my $csv = Text::CSV_XS->new();  

my $file = 'Boston 1 Minute Moisture Data.csv';  

my $sum = 0;  
open(my $data, '<', $file) or die "Could not open '$file'\n";  
while (my $line = <$data>) {  
    chomp $line;  

    if ($csv->parse($line)) {  
        my @columns = $csv->fields();  

        $sum += $columns[3];  
    } else {  
        warn "Line could not be parsed: $line\n";  
    }  
}  
print "$sum\n";  

结果是get是“Line无法解析:$ line \ n”。由于某种原因,解析器不解析字段。有什么想法吗?

我也尝试了以下代码:

#!/usr/bin/perl  
use strict;  
use warnings;  
use Text::CSV;  

my $file = 'Boston 1 Minute Moisture Data.csv';  
my $csv = Text::CSV->new();  

open (CSV, "<", $file) or die $!;  

while (<CSV>) {  
    if ($csv->parse($_)) {  
        my @columns = $csv->fields();  
        #print "@columns\n";  
        print fields[1];  
        } else {  
        my $err = $csv->error_input;  
        print "Failed to parse line: $err";  
    }  
}  
close CSV;  

我对文件中的每一行都得到以下结果:

  

print()在test2.pl第16行第9326行的未打开的文件句柄字段上。

1 个答案:

答案 0 :(得分:2)

问题解决方案的整体结构如下:

  • open cities.txt
  • 从cities.txt读取的每一行
    • 打开“$ city 1 Minute Moisture Data.txt”文件
    • 来自湿度文件的每一行
      • 如果该行的日期在范围内
      • 将该行添加到保存文件

您尚未指定每个城市是否有单独的保存文件。

您的试用解决方案正确使用Text::CSV模块 - 这很好。您还需要某种方法来解析日期值 - 输入值(开始和结束日期)和扫描值(来自水分数据)。我可能会使用POSIX::strptime模块,但您可以使用任何其他日期和时间操作模块。

这不是很好的Perl - 但是下面的代码似乎在运行时起作用:

$ perl scan.pl 1/3/1999 30/4/1999
Boston,1/4/1999,9:31:00 AM,blah, blah, blah  
Boston,1/4/1999,9:32:00 AM,blah, blah, blah  
Boston,1/4/1999,9:33:00 AM,blah, blah, blah
Atlanta,1/4/1999,9:31:00 AM,blah, blah, blah  
Atlanta,1/4/1999,9:32:00 AM,blah, blah, blah  
Atlanta,1/4/1999,9:33:00 AM,blah, blah, blah
Seattle,1/4/1999,9:31:00 AM,blah, blah, blah  
Seattle,1/4/1999,9:32:00 AM,blah, blah, blah  
Seattle,1/4/1999,9:33:00 AM,blah, blah, blah
$ perl scan.pl 1/3/2000 30/4/2000
$

(考虑到问题中的城市数据,以及每个城市的示例数据的副本。我假设正常(例如英国)样式日期与序列日,月,年。如果您正在使用美式风格日期,您需要进行调整。如果您使用无效日期,您将收到错误; get_date()中的错误处理不存在。)

#!/usr/bin/env perl
use strict;
use warnings;
use POSIX::strptime;
use Text::CSV;

my $cities = "cities.txt";

die "Usage: $0 start-date end-date\n" if scalar(@ARGV) != 2;

my $start = get_date($ARGV[0]);
my $end   = get_date($ARGV[1]);

{
    open my $cfh, "<", $cities or die "Failed to open $cities ($!)";
    while (<$cfh>)
    {
        chomp;
        my $city = $_;
        $city =~ s/\s*,.*//;
        $city =~ s/^\s*//;
        my $moisture = "$city 1 Minute Moisture Data.txt";
        open my $mfh, "<", $moisture or die "Failed to open $moisture ($!)";
        process_file($mfh, $moisture, $city);
    }
}

sub get_date
{
    my($str) = @_;
    my ($mday, $mon, $year) = ( POSIX::strptime($str, '%d/%m/%Y') )[3,4,5];
    return (($year + 1900) * 100 + ($mon + 1)) * 100 + $mday;
}

sub process_file
{
    my($fh, $file, $city) = @_;
    my $csv = Text::CSV->new() or die "Failed to create Text::CSV object";
    my $line = <$fh>;
    die "Unexpected EOF in $file" unless defined $line;
    while ($line = <$fh>)
    {
        chomp $line;
        die "Failed to parse line <<$line>>" unless $csv->parse($line);
        my @columns = $csv->fields();
        die "Insufficient columns in <<$line>>" if scalar(@columns) < 1;
        my $date = get_date($columns[0]);
        print "$city,$line\n" if ($date >= $start && $date <= $end);
    }
}