使用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