如何将文本文件的每一行写入CSV行

时间:2014-02-15 08:30:51

标签: perl csv

我是perl的新手,我正在尝试获取一个文件(它实际上是一个.idx文件),格式如下

 Monday       Foo Name             43212    
 Tuesday      Name Foo Foo         43252
 Tuesday      Name                 50322 
 Wednesday    Foo Name             53221
 Thursday     Foo Bar Foo Name     24353

我希望将其输出为csv文件。该文件应该看起来与此类似,但CSV格式除外,以便Excel可以读取它。另外,我只希望包含星期二的行,以便CSV文件看起来像

 Tuesday      Name Foo Foo     43252
 Tuesday      Name             50322

在Excel中。我还有idx格式的formYYYY_Q.idx个文件,其中YYYY表示年份,Q表示该季度。我想循环遍历我拥有的所有.idx个文件,并创建一个大型CSV文件,每个.idx文件中只有行,开头是Tuesday。我到目前为止的代码是

 #!/usr/bin/perl

 use warnings;
 use strict;
 use Text::CSV;

 my $csvfile= Text::CSV->new({binary=>1,auto_diag=>1});
 $csvfile->column_names("Day","Name","Number");

 my @datalines;
 my $idxfile="form1994_1.idx";

 open(INFILE, "< $idxfile") or die $!;
 open(my $outfile, "> Master.csv") or die $!;

 while(<INFILE>){

      if(/^Tuesday/){

            chomp($_);
            push(@nsarlines, $_);

     }

 }

当我用open(OUTFILE, "> Master.txt") or die $!;替换open命令并将其包含在while循环之外的最后一行代码中时:

 print OUTFILE map {"$_ \n"} @nsarlines;

Master.txt文件看起来好像我想要它

 Tuesday      Name Foo Foo     43252
 Tuesday      Name             50322

但是,如果我使用上面写的open命令在while循环之外做这样的事情:

 $csvfile-> print($outfile, \@test);

我得到一个包含整个$_字符串的CSV文件,该字符串是Master.csv文件每个单元格中.idx文件的一行。我很难弄清楚如何使perl使每个.idx行成为CSV行,并且手动将逗号插入$_(不是优雅或理想的选项)。

我需要做的第二件事是我将文件formYYY_Q.idx全部放在同一目录中,我想自动浏览每一个文件,拉出以星期二开头的行并将其添加到主文件中。 csv文件(或者更确切地说,完成所有这些并在最后编写一次Master.csv文件)。我认为File::Find可能会这样做,但我无法弄清楚如何使用它。

感谢您的帮助。

3 个答案:

答案 0 :(得分:2)

有两种方法可以使用File :: Find。一种是使用wanted函数将有关文件的数据添加到全局列表/队列/变量,然后在find调用后处理数据。另一种方法是在wanted函数中执行所有处理。

我个人不喜欢使用全局变量来传递数据,但遗憾的是File :: Find是你的选择。以下是他们这样做的示例:http://www.perlmonks.org/?node_id=217378。在该示例中,他们使用%size哈希将数据传递出匿名wanted函数。这可能是您的最佳选择,您可以将匹配的文件名添加到全局列表中,然后遍历列表,将每个文件中的数据写入CSV。

另一个选项是在wanted功能中执行处理。但同样这也不是最理想的,因为您需要使用全局变量将信息传递到关于打开的CSV文件的wanted函数。

另一种选择,假设所有.idx文件都可以保证在同一目录中(而不是在同一个目录树中)是使用opendir&amp; readdir函数。 http://perldoc.perl.org/functions/readdir.html

my $dh;   # directory handle
opendir($dh, $your_dir) || die $!;
my @idxfiles = grep {/\.idx$/} readdir($dh);
closedir($dh);

foreach my $idxfile (@idxfiles) {
   open(INFILE, "< $idxfile") or die $!;
   ... the rest of your code ...
}

答案 1 :(得分:1)

结合了一些操作代码和一些avitevet的代码,并提出了这个:

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

opendir(DIR, ".") or die $!;
my @idxfiles = sort(grep {/\.idx$/} readdir(DIR));
closedir(DIR);

open(OUT, "> Master.csv") or die $!;
foreach my $idx (@idxfiles) {
  open(F, "$idx") or die $!;
  while (<F>) {
    if (m/^Tuesday/) {
      my @fields = split(/\s+/);
      my $day = shift(@fields); # grab the first one
      my $zip = pop(@fields); # grab the last one;
      my $middle = join(" ", @fields); # merge the rest back together
      print OUT "$day,$middle,$zip\n";
    }
  }
  close(F)
}
close(OUT);

答案 2 :(得分:0)

我赞赏您对问题的清晰描述以及您尝试过的解决方案!

鉴于您的叙述,包括将所有idx文件放在一个目录中,请考虑以下解决方案 - 在包含* .idx的目录中执行:

use strict;
use warnings;

open my $outfile, '>', 'Master.csv' or die $!;
print $outfile "Day,Name,Number\n";

for my $idxfile (<*.idx>) {
    next unless $idxfile =~ /^form\d{4}_\d\.idx/;
    open my $infile, '<', $idxfile or die $!;

    while (<$infile>) {
        if (/^Tuesday/) {
            my ( $day, $name, $num ) = /(\w+)\s+(.+?)\s+(\d+)/;
            print $outfile "$day,$name,$num\n";
        }
    }

    close $infile;
}

close $outfile;

首先将标头写入Master.csv文件。 <*.idx>构造是glob,它从当前目录生成* .idx文件列表。接下来,使用正则表达式确保文件名符合您的命名规范。如果只有那些要处理的文件在目录中,则可以删除该行。

打开并处理当前的idx文件。与在代码中一样,正则表达式用于检查行开头的“星期二”。如果遇到这样的行,正则表达式会捕获三个字段:

/(\w+)\s+(.+?)\s+(\d+)/
   ^   ^  ^    ^   ^
   |   |  |    |   |
   |   |  |    |   + - One+ digits - Number
   |   |  |    + - One+ whitespaces
   |   |  + - One+ any characters (except newline) - Name
   |   + - One+ whitespaces 
   + - One+ 'word' characters - Day

这些捕获的字段 - 用逗号分隔它们 - 被写入Master.csv文件。当完整读取当前的idx文件时,它将被关闭,并处理下一个idx文件 - 如果有的话。最后,Master.csv文件已关闭。

希望这有帮助!