使用Perl查找并修复CSV文件中的错误

时间:2014-08-14 16:37:14

标签: 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,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++ . "," . "$_";
}

上面的代码应该给我错误的行,但是能够从原件中提取它们是超出我的。上面的代码还包含一些语法错误。

1 个答案:

答案 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.csv2.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