使用Perl查找和修复CSV文件中的错误
我正在处理大量的数据。偶尔会有一个失误。我想在我选择的条件下,识别每一行的错误。因此,我需要行号以及每个错误行的行号。我将在少数几个文件上运行这个脚本,并希望将报告输出到其中一个文件 下面是我的示例数据:使用Perl查找和修复CSV文件中的错误,perl,Perl,我正在处理大量的数据。偶尔会有一个失误。我想在我选择的条件下,识别每一行的错误。因此,我需要行号以及每个错误行的行号。我将在少数几个文件上运行这个脚本,并希望将报告输出到其中一个文件 下面是我的示例数据: 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,william
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;
而(){
除非($F[4]=~/\d+[-]\d+[-]\d+/)
打印“行”#“$count++.”、“$”;
}
上面的代码应该给我错误的行,但是我无法从原始行中提取它们。上面的代码还包含一些语法错误。这将按照您的要求执行 请确保数据中的任何字段都不能包含逗号
,
,否则您将需要使用来处理它,而不仅仅是简单的拆分
更新
如果要处理多个文件,则需要此文件
循环结束时的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;
}
这就照你说的做
请确保数据中的任何字段都不能包含逗号,
,否则您将需要使用来处理它,而不仅仅是简单的拆分
更新
如果要处理多个文件,则需要此文件
循环结束时的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;
}
您没有解释修改后的文件包含的内容。查找包含错误的行很简单,但自动修复它们几乎是不可能的。您所能期望的最好方法是删除错误数据。请解释您作为示例显示的数据。它看起来像一个单独的文件,但第一列显示每一行来自不同的源。那是怎么回事?很抱歉@borodin忽略了这一点。不管怎样,我都希望有相同的文件源。那么,在您发布的下面的脚本中,您是否可以对其进行编辑,以显示如何在多个文件上运行脚本?然后您应该更正您的问题。我已经在我的答案中添加了说明如何处理多个输入文件。我仍然不明白4.txt
等文件中应该有什么内容。你最后的评论应该在我的答案下面。如果你解释一下这个问题,我相信它是可以解决的。请不要放弃任何人的答案,写下你自己的作品。作者花了很多时间编写了一个帮助您的解决方案,如果您不让他们完成任务,这将是一个挫折。您正在使用Windows系统吗?如果是这样,那么这就是问题所在。您没有解释修改后的文件包含的内容。查找包含错误的行很简单,但自动修复它们几乎是不可能的。您所能期望的最好方法是删除错误数据。请解释您作为示例显示的数据。它看起来像一个单独的文件,但第一列显示每一行来自不同的源。那是怎么回事?很抱歉@borodin忽略了这一点。不管怎样,我都希望有相同的文件源。那么,在您发布的下面的脚本中,您是否可以对其进行编辑,以显示如何在多个文件上运行脚本?然后您应该更正您的问题。我已经在我的答案中添加了说明如何处理多个输入文件。我仍然不明白4.txt
等文件中应该有什么内容。你最后的评论应该在我的答案下面。如果你解释一下这个问题,我相信它是可以解决的。请不要放弃任何人的答案,写下你自己的作品。作者花了很多时间编写了一个帮助您的解决方案,如果您不让他们完成任务,这将是一个挫折。您正在使用Windows系统吗?如果是,那么这就是问题所在。@JDE876如果是
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