awk vs perl在阅读csv?

时间:2014-03-30 04:12:32

标签: perl awk

我在这里需要速度。我有一个500mb大小的csv文件(实际上有很多csv,但我现在只考虑一个) 我需要阅读第3列并从中选择唯一的字符串。我测试过的以下方法,我发现只有awk是最快的

使用perl:

  • 使用传统方式读取文件打开文件并传递 while循环的文件句柄
  • 取消设置$ /变量并将整个文件粘贴到字符串中然后 拆分线并选择第3个字段并继续
  • 使用File :: Slurp模块
  • 使用Text :: CSV / XS来阅读csv并做必要的

在上述所有方法中,解析csv大约需要500秒。

但如果我尝试使用awk做同样的事情,它会在近10秒内完成。

我仍在学习perl中的步骤,最近我对它的热情在看到它的哈希功能后增长了很多。但是这个问题占了我的位置 背部。这是unix工具占上风的perl的一些限制吗?

我知道Text :: CSV是处理csv文件的最佳方式。但速度是我关注的问题,我可以保证我的csv没有任何嵌入式逗号或其他问题,只有Text :: CSV可以处理。

更新:发现我的问题

my %hash;
my $file = $ARGV[0] or die "Need input CSV $!\n";
open(my $fh,'<',$file) or die "Could not open the $file $!\n";
while(my $line = <$fh>)
{
chomp($line);
my $field2=(split /|/, $line)[2];  #I missed to quote the pipe delimiter
$hash{$field2}++;
}
print "$_\n" for keys %hash;

另一个更新:发布并修复

我的csv由&#39; |&#39;分隔。我错过了引用它们。因此,执行时间显着减慢,而且它产生的输出是错误的,我没有注意到。引用分隔符后,脚本能够在大约18秒内完成,当我使用@Borodin逻辑限制字段分割时,执行时间进一步减少。我能达到与awk相同的速度。

我仍然发现Text :: CSV方法较慢,因为我的文件可以使用默认的分割方法,我将继续使用它。

2 个答案:

答案 0 :(得分:6)

我生成了一个350 MiB文件,其10,000,000行类似于:

part1,part2,data856801,part4,part5

(其中第三列中的数字是100,000到999,999之间的随机值)并使用了自制的Perl 5.18.1:

time perl -n -a -F, -l -e '$a{$F[2]}++;
           END { foreach $key (sort keys %a) {print "$key";} }' junk.data >junk.perl.output

这花了大约34秒。没有sort,花了大约33秒(我在时间上有一些变化)。系统提供的Perl 5.16.2的时间基本相同。

为了比较,使用BSD awk(20070501):

time awk -F, '{a[$3]++} END {for (key in a) print key}' junk.data > junk.awk.output

这花了大约29秒,以未排序的顺序产生数据。 GNU awk 3.1.7花了大约15秒(显着更快)。

只需在文件上使用catcp只需5秒钟。

所有过滤后的输出文件中都有899993行;一致性很好。

因此,对于这项工作来说,Perl似乎比awk略慢,但在我的机器上不是50倍。我不确定在Perl脚本上可以进行多少优化;我所写的内容非常简单粗暴。

测试:

  • MacBook Pro(2011年初),2.3 GHz Intel Core i7。
  • 16 GiB 1333 MHz DDR3 RAM。
  • 750 GiB 5400 rpm磁盘。
  • Mavericks 10.9.2
  • Perl 5.18.1(5.16.2)
  • BSD awk 20070501
  • GNU awk 3.1.7

我让iTunes在后台播放音乐,并在浏览器中输入,因此在测试运行时系统没有闲置。


使用Text :: CSV with Text :: CSV_XS和以下脚本,花了将近49秒:

#!/usr/bin/env perl
use strict;
use warnings;

use Text::CSV;

my %a;
my $csv = Text::CSV->new ( { binary => 1 } )  # should set binary attribute.
                   or die "Cannot use CSV: ".Text::CSV->error_diag ();

open my $fh, "<:encoding(utf8)", "junk.data" or die "junk.data: $!";
while ( my $row = $csv->getline( $fh ) )
{
    $a{$row->[2]}++;
}
$csv->eof or $csv->error_diag();
close $fh;

print "$_\n" for keys %a;

Perl跑得快

有趣的是,Borodin script大约需要17秒,比Perl-as - awk模式操作快一点。知道Perl是否设法优化拆分会很有趣,因为它知道它只需要第三个字段,而awk模式必须在每一行中拆分五个字段(对于样本文件),即使只有第三是使用。

这与GNU awk时间非常相似。

答案 1 :(得分:3)

如果您显示了Perl代码,将会有所帮助。对于您描述的文件,不需要Text::CSV的开销,其中数据从不包含逗号(并且可能没有引号?)

你的程序看起来应该是这样的

use strict;
use warnings;
use autodie;

open my $fh, '<', 'myfile';

my %data;

while (<$fh>) {
  my $col3 = (split /,/, $_, 4)[2];
  ++$data{$col3};
}

print "$_\n" for keys %data;