我有一个读取CSV文件的进程,我想在开始解析之前确保它是正确的。
我得到一个文件名,检查它是否存在,然后检查其完整性。如果它不存在或没有正确的CSV文件,那么我尝试前一天的文件
有没有办法检查文件是否是正确的CSV文件?我正在使用Text::CSV_XS
来解析它。
谷歌搜索了一下我在Text::CSV_XS
Git repo上找到了this csv-check example code。它看起来像我可以使用的东西。
答案 0 :(得分:3)
无论如何都无法读取和解析中的每个记录,无法测试文件的有效性。
我建议的方法是处理您找到的每个文件,在内存中构建您希望在数据库中结束的数据,如果您发现错误,则只需丢弃它并尝试使用下一个文件。
一旦到达文件的末尾并知道它是有效且完整的,那么您只需将准备好的数据保存到数据库,然后继续下一个文件。
这将正常工作,除非您的CSV文件巨大并且太大而无法合理地放入内存中。在这种情况下,你应该只需要两次通过。
答案 1 :(得分:3)
正如其他人所说,您必须解析整个文件以确定它是否有效。你也可以一石二鸟,同时进行数据处理和错误检查。
getline()
在达到EOF时返回undef
,或者无法解析一行。您可以使用它来解析文件,如果有任何解析错误则暂停:
while ( my $row = $csv->getline($io) ) {
# Process row
}
$csv->eof or do_something();
你也可以
use autodie;
或将Text::CSV_XS->new()
中的auto_diag
选项设置为die
错误:
$csv = Text::CSV_XS->new({ auto_diag => 2 });
您可以通过将解析代码包装在eval
块中来处理错误。此方法将在error_diag()
之前自动调用die
,将错误打印到stderr;这可能不是你想要的。
如果检测到错误,如何“恢复”对先前行所做的处理?如果您的数据库引擎支持它们,则一种可能性是数据库事务。开始处理文件时,启动事务。如果出现解析错误,只需回滚事务并转到下一个文件;否则,提交交易。
顺便说一句,我还没有看到你的代码插入数据库记录,所以我不确定这是否适用,但是对每一行都有一个单独的insert语句效率不高。相反,请考虑在解析文件时构造复合插入语句;或者,对于非常大的文件,让数据库使用MySQL LOAD DATA INFILE之类的东西进行解析(只是一个例子,因为我不知道你正在使用什么DBMS)。
要使用复合插入,请在内存中构建查询语句,如Borodin suggested。如果到达文件的末尾而没有任何解析错误,请执行语句;否则,扔掉它然后转到下一个文件。
对于非常大的文件,让数据库进行解析可能是最快的,尤其是在插入数据之前进行最少的处理时。例如,MySQL的LOAD DATA INFILE将在检测到数据解释或重复键错误时停止。如果将语句包装在事务中,则可以在出现错误时回滚并尝试加载下一个文件。这种方法的优点是加载有效文件的速度非常快,比必须先用Perl解析它们要快得多。
答案 2 :(得分:2)
这就是我所做的,如果文件正常则sub返回1,如果不正常则返回0:
sub CheckCSVFile {
my ($fileName) =@_;
my $csv = Text::CSV_XS->new();
open my $in_fh, '<:encoding(ISO-8859-1)', $fileName;
while ( <$in_fh> ) {
my $status = $csv->parse($_);
if ($status != 1){
return $status;
}
}
$csv->eof;
close $in_fh;
return 1;
}
我事先检查文件是否存在,所以不应该出错。如果出现问题我也不想退出。这有点粗糙,但对我有用。