我正在处理大量数据。不时有一个滑倒。我希望在我选择的条件下识别出每一行都有错误。有了这个,我想要行号和每个错误行的行号。我将在一些文件上运行此脚本,我将要将报告输出到一个。
所以这是我的示例数据:
File_source,ID,Name,Number,Date,Last_name
1.csv,1,Jim,9876,2014-08-14,Johnson
1.csv,2,Jim,9876,2014-08-14,smith
1.csv,3,Jim,9876,2014-08-14,williams
1.csv,4,Jim,9876,not_a_date,jones
1.csv,5,Jim,9876,2014-08-14,dean
1.csv,6,Jim,9876,2014-08-14,Ruzyck
期望的输出:
Row#5,4.csv,4,Jim,9876,not_a_date,jones (this is an erroneous row)
如果日期字段中的任何内容不是日期,我选择的条件是打印输出。
如您所见,我想要的输出包含发生错误的行号以及数据本身。
在我的输出显示每个文件中出错的行之后,我想从未触摸的原始CSV文件中获取该行以重做(修改后的原始文件和原始文件包含相同数量的行)。在我有这些重做行的文件后,我可以省略并清理所需的位置,以防止导入中断。
文件夹结构将包含:
Modified: 4.txt
Original: 4.csv
我在这里开始用Perl编写的东西,逻辑上至少会返回我需要的行。但是我相信我的语法有点偏,我不知道如何插入其他子程序。
代码:
$count = 1;
while (<>) {
unless ($F[4] =~ /\d+[-]\d+[-]\d+/)
print "Row#" . $count++ . "," . "$_";
}
上面的代码应该给我错误的行,但是能够从原件中提取它们是超出我的。上面的代码还包含一些语法错误。
答案 0 :(得分:4)
这可以按照你的要求进行。
请确保数据中的所有字段都不能包含逗号,
,否则您需要使用Text::CSV
来处理它而不仅仅是简单的split
}。
use strict;
use warnings;
use 5.010;
use autodie;
open my $fh, '<', 'example.csv';
<$fh>; # Skip header
while (<$fh>) {
my @fields = split /,/;
if( $fields[4] !~ /^\d{4}-\d{2}-\d{2}$/ ) {
print "Row#$.,$_";
}
}
<强>输出强>
Row#5,4.csv,4,Jim,9876,not_a_date,jones
<强>更新强>
如果你想处理一些文件,那么你需要这样做。
循环结束时的close ARGV
存在,以便行计数器$.
重置为
1在每个文件的开头。如果没有它,它只会在所有文件中从1向上继续。
你会像
那样运行rob@Samurai-U:~$ perl findbad.pl *.csv
或者您可以单独列出文件,用空格分隔。
对于测试,我创建了文件1.csv
和2.csv
,它们与您的示例数据相同,只是每行的第一个字段是包含数据的文件的名称。
您可能不希望输出中的行宣布每个文件名,在这种情况下,您应该只用if
替换整个第一个next if $. == 1
块。
use strict;
use warnings;
@ARGV = map { glob qq{"$_"} } @ARGV; # For Windows
while (<>) {
if ($. == 1) {
print "\n\nFile: $ARGV\n\n";
next;
}
my @fields = split /,/;
unless ( $fields[4] =~ /^\d{4}-\d{2}-\d{2}$/ ) {
printf "Row#%d,%s", $., $_;
}
close ARGV if eof ARGV;
}
<强>输出强>
File: 1.csv
Row#5,1.csv,4,Jim,9876,not_a_date,jones
File: 2.csv
Row#5,2.csv,4,Jim,9876,not_a_date,jones