Mik*_*ike 26 csv perl parsing text split
我正在开发一个涉及在Perl中解析大型csv格式文件的项目,并希望提高效率.
我的方法是split()首先按行显示文件,然后再用split()逗号分隔每行以获取字段.但这是次优的,因为至少需要两次传递数据.(一次用线分开,然后再用每一行分开).这是一个非常大的文件,因此切割加工一半将是整个应用程序的重大改进.
我的问题是,使用内置工具解析大型CSV文件的最有效时间是什么?
注意:每一行都有不同数量的标记,因此我们不能只忽略行并仅用逗号分割.此外,我们可以假设字段将只包含字母数字ascii数据(没有特殊字符或其他技巧).此外,我不想进行并行处理,尽管它可能有效.
编辑
它只能涉及Perl 5.8附带的内置工具.出于官僚主义的原因,我不能使用任何第三方模块(即使托管在cpan上)
另一个编辑
假设我们的解决方案只允许在文件数据完全加载到内存后处理它们.
还有另一个编辑
我刚刚抓住这个问题是多么愚蠢.抱歉浪费你的时间.投票结束.
Mic*_*man 46
正确的方法 - 按一个数量级 - 使用Text :: CSV_XS.它将比您自己可能做的任何事情更快,更强大.如果您决定仅使用核心功能,则根据速度与稳健性的不同,您有几个选项.
关于pure-Perl的最快速度是逐行读取文件,然后天真地分割数据:
my $file = 'somefile.csv';
my @data;
open(my $fh, '<', $file) or die "Can't read file '$file' [$!]\n";
while (my $line = <$fh>) {
chomp $line;
my @fields = split(/,/, $line);
push @data, \@fields;
}
Run Code Online (Sandbox Code Playgroud)
如果任何字段包含嵌入的逗号,则会失败.更健壮(但更慢)的方法是使用Text :: ParseWords.为此,请替换为split:
my @fields = Text::ParseWords::parse_line(',', 0, $line);
Run Code Online (Sandbox Code Playgroud)
jkr*_*mer 19
这是一个也尊重报价的版本(例如foo,bar,"baz,quux",123 -> "foo", "bar", "baz,quux", "123").
sub csvsplit {
my $line = shift;
my $sep = (shift or ',');
return () unless $line;
my @cells;
$line =~ s/\r?\n$//;
my $re = qr/(?:^|$sep)(?:"([^"]*)"|([^$sep]*))/;
while($line =~ /$re/g) {
my $value = defined $1 ? $1 : $2;
push @cells, (defined $value ? $value : '');
}
return @cells;
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它:
while(my $line = <FILE>) {
my @cells = csvsplit($line); # or csvsplit($line, $my_custom_seperator)
}
Run Code Online (Sandbox Code Playgroud)
正如其他人提到的,正确的方法是使用Text :: CSV,Text::CSV_XS后端(用于FASTEST读取)或Text::CSV_PP后端(如果无法编译XS模块).
如果你被允许在本地获得额外的代码(例如,你自己的个人模块),你可以Text::CSV_PP把它放在本地的某个地方,然后通过use lib变通方法访问它:
use lib '/path/to/my/perllib';
use Text::CSV_PP;
Run Code Online (Sandbox Code Playgroud)
另外,如果没有其他选择将整个文件读入内存并且(我假设)存储在标量中,您仍然可以通过打开标量句柄来读取文件句柄:
my $data = stupid_required_interface_that_reads_the_entire_giant_file();
open my $text_handle, '<', \$data
or die "Failed to open the handle: $!";
Run Code Online (Sandbox Code Playgroud)
然后通过Text :: CSV接口读取:
my $csv = Text::CSV->new ( { binary => 1 } )
or die "Cannot use CSV: ".Text::CSV->error_diag ();
while (my $row = $csv->getline($text_handle)) {
...
}
Run Code Online (Sandbox Code Playgroud)
或逗号上的次优分割:
while (my $line = <$text_handle>) {
my @csv = split /,/, $line;
... # regular work as before.
}
Run Code Online (Sandbox Code Playgroud)
使用此方法,数据一次只能从标量中复制一点.
| 归档时间: |
|
| 查看次数: |
69832 次 |
| 最近记录: |