我是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
可能会这样做,但我无法弄清楚如何使用它。
感谢您的帮助。
答案 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文件已关闭。
希望这有帮助!