Perl中最快的CSV解析器

Perl中最快的CSV解析器,perl,parsing,csv,Perl,Parsing,Csv,我正在创建一个子例程: (1) 解析CSV文件 (2) 并检查该文件中的所有行是否具有预期的列数。如果列数无效,它会发出咯咯声 当行数从数千行到数百万行时,您认为最有效的方法是什么? 现在,我正在尝试这些实现 (1)基本文件解析器 open my $in_fh, '<', $file or croak "Cannot open '$file': $OS_ERROR";

我正在创建一个子例程:

(1) 解析CSV文件

(2) 并检查该文件中的所有行是否具有预期的列数。如果列数无效,它会发出咯咯声

当行数从数千行到数百万行时,您认为最有效的方法是什么?

现在,我正在尝试这些实现

(1)基本文件解析器

open my $in_fh, '<', $file or 
    croak "Cannot open '$file': $OS_ERROR";                                                            
                                                                                                      
my $row_no = 0;                                                                                           
while ( my $row = <$in_fh> ) {                                                                            
    my @values = split (q{,}, $row);                                                                      
    ++$row_no;                                                                                            
    if ( scalar @values < $min_cols_no ) {                                                                
        croak "Invalid file format. File '$file' does not have '$min_cols_no' columns in line '$row_no'.";
    }                                                                                                     
}                                                                                                         
                                                                                                      
close $in_fh                                                                                              
    or croak "Cannot close '$file': $OS_ERROR";                                                           
这些是我得到的数字(以秒为单位)

1000行文件:

实施1:0.0016

实施2:0.0025

实施3:0.0050

实施4:0.0097

10000行文件:

实施1:0.0204

实施2:0.0244

实施3:0.0523

实施4:0.1050

1500000行文件:

实施1:1.8697

实施2:3.1913

执行情况3:7.8475

执行情况4:15.6274

考虑到这些数字,我认为简单解析器是最快的,但从我从不同来源所读到的资料来看,Text::CSV_XS应该是最快的


有人能告诉我这件事吗?我使用模块的方式有问题吗?非常感谢你的帮助

所有CSV解析模块都做同样的事情:打开文件并以某种方式解析CSV,就像您在基本sub中所做的那样。它们只会带来更多的开销,因为在内部,它们所做的远远超过您的需要(检查正确的CSV格式、传递对象结构等)。这使得他们在不同程度上比你的基本方法慢

你自己对这些方法进行了基准测试;结果不是很明显吗?如果我不需要CSV模块的扩展功能,我会自己以基本方式解析CSV文件


(我不知道您是否可以通过提高模块的使用率来加速它们)

请注意,您的
Text::csvxs
版本比简单的解析器版本做得更多。它分割行,将其放入内存,并使hashref指向字段

它可能还有其他逻辑,比如允许转义分隔符(我不知道,因为我没有使用它)。除此之外,在使用模块时,总是会有少量开销:函数调用、来回传递参数,以及可能不适用于您的情况的通用代码(例如,对您不关心的事情进行错误检查)

通常,使用模块的好处远远大于成本。您可以获得更多的特性、更可靠的代码等,但对于一个非常简单的小任务来说,情况可能并非如此。如果您只需要验证列的数量,那么使用模块可能会有些过分。您只需计算列的数量,而不必费心拆分,就可以更快地实现自己的实现:

/(?:,[^,]*){$min_cols_no-1}/ or croak "Did not find minimum number of columns";
如果您打算在验证步骤之外进行实际处理,那么使用该模块可能会有所帮助。

有CSV文件

header1,header2,header3
value1,value2,value3
还有CSV文件

header1,"This, as they say, is header2","And header3
even contains a newline!"
value1,"value2, 2nd in a series of 3 values",value3

Text::CSV
及其同类产品经过了精心开发和测试,以应对第二类问题。如果您确信您的输入确实并且始终符合简单的CSV规范,那么很可能您可以构建一个性能优于
Text::CSV

的解析器,只是为了好玩,我测试了regexp。。。而且它是有效的!;)如果您有足够的ram,您可以一次读取整个文件,然后使用正则表达式:

my $blob = 'a;s;d
q;w;e
r;t;y
u;i;o
p;z;x
c;;b
n;m;f
g;h;j
k;l;';

say $blob =~ /^ ([^;]*;){2}[^;]* (\n (([^;]*;){2}[^;]*)+ \n ([^;]*;){2}[^;]*)? $/x ? 'ok' : 'bu';

但这不包括分隔符转义、引用等-只需测试指定数量的分隔符即可:)

为什么速度对您如此重要?你确定这是你代码中的慢点吗?你的程序速度有问题吗?你确定你没有过早地优化吗?例如,如果你的程序花费90%的时间写入数据库,10%的时间读取CSV文件,那么找到最快的CSV解析器并不是你花费时间的最佳方式。
““这就是字符串,”他说,“你可以在引号中包含逗号、句点和所有其他类型的东西!”
@AndyLester,速度是如此重要,因为这是我们拥有的几乎所有系统的当前工作流的一个附加功能或步骤,如果可能的话,我们不想增加任何额外的时间。在这种情况下,我真的很想向专家们学习我的做法是否正确。我也同意你的看法。优化对数据库的写入确实更有意义,因为大多数时候,瓶颈都发生在那里。仅供参考,我们正在使用freebcp插入。再次感谢你,安迪@杰克曼尼,我在谷歌上搜索了部分陈述,但找不到来源。你从哪儿弄来的?谢谢@卡莱尔:我知道你“如果可能的话,不想再增加时间。”没有人愿意。但实际上,这是一个“任何额外的时间”意味着什么的问题。如果在任何更改之前37分钟内运行某个程序,并且在37:02使用Text::CSV::Whatever时运行该程序,那么是否存在可以使其在37:01运行的程序有关系?花大量的程序员时间去弄清楚这一点值得吗?或者使用一个模块,要求您编写不易理解的代码?当然,如果没有测量,你无法回答任何问题。我想确认我的方法是正确的,我没有遗漏任何东西。现在,我真的明白了,就像@dan1111所说的,“通常情况下,使用模块的好处远远大于成本。”谢谢!我现在更明白了。谢谢,@mob!
/(?:,[^,]*){$min_cols_no-1}/ or croak "Did not find minimum number of columns";
header1,header2,header3
value1,value2,value3
header1,"This, as they say, is header2","And header3
even contains a newline!"
value1,"value2, 2nd in a series of 3 values",value3
my $blob = 'a;s;d
q;w;e
r;t;y
u;i;o
p;z;x
c;;b
n;m;f
g;h;j
k;l;';

say $blob =~ /^ ([^;]*;){2}[^;]* (\n (([^;]*;){2}[^;]*)+ \n ([^;]*;){2}[^;]*)? $/x ? 'ok' : 'bu';