当只需要字段的子集时,分割长行的有效方法是什么

时间:2019-06-19 14:58:23

标签: perl csv text-parsing string-parsing perl5.30

查询的详细信息如下:

  1. 我有一个非常大的TSV(制表符值)文件(其中大于30 GB)。
  2. 我想从此文件中提取某些行,这些行不以空的last字段结尾。由于这是一个TSV文件,所以那些不以\t\n结尾的行,这是一个微不足道的测试,而不是此问题的主题。这样一来,就可以立即删除大约75%的生产线,从而减轻工作量。
  3. 然后,我想从剩余的行中提取一小部分字段。这些字段不是连续的,但是数量很少(例如,假设总共30多个字段中有7个)。例如,说出2,3,12-18,25-28,31字段。
  4. 我要提取的行很长,大多数行长达1000个字符,因为它们包含大量制表符分隔的字段。

一种选择是显然使用以下简单的代码,我已经尝试过将其很好地格式化并包括注释以显示我的推理:

use warnings;
use strict;
# I am using the latest stable version of Perl for this exercise
use 5.30.0;

while (<>)
{
  # Skip lines ending with an empty field
  next if substr($_,-2) eq "\t\n";

  # Remove "\n"
  chomp;

  # Split matching lines into fields on "\t", creating @fields
  my @fields=split(/\t/,$_);

  # Copy only the desired fields from @fields to create a new
  # line in TSV format
  # This can be done in one simple step in Perl, using
  # array slices and the join() function
  my $new_line=join("\t",@fields[2,3,12..18,25..28,31]);

  # ...
}

但是,使用split会导致额外的解析(超出了我需要的最后一个字段),并生成了我也不需要的完整字段数组。我认为不创建数组,而是解析每行以寻找制表符并在我进行操作时对字段索引进行计数,在创建途中创建输出行,并在我需要的最后一个字段处停止,会更有效率。

我是在评估中纠正还是只是做一个简单的split,然后是包含感兴趣字段的join切片,那么从性能角度来看,这是最好的方法吗?

更新:不幸的是,没有人提到使用GNU cut进行拆分并将结果通过管道传输到Perl进行其余处理的可能性。这可能是最高效的方式,无需编写大量的自定义(C)代码来执行此操作,也无需借助自定义行解析(也使用C)来进行基于大型块的读取。

2 个答案:

答案 0 :(得分:5)

您可以使用其limit参数告诉split何时停止:

std::forward<O>(o).value()

(指定一个多于您实际想要的字段数,因为它产生的最后一个字段将包含该行的其余部分。)

答案 1 :(得分:0)

grep -P -v "\t\s*$" yourFile.tsv | cut -f2,3,12-18,25-28,31

您甚至不必为此编写Perl代码。

在这里

-P是“ perl grep”,它为朴素的grep提供了更多功能。

-v是逆向匹配,与您的next if

相对应

顺便说一句,如果您有足够的核心和内存,那么您可能希望通过拆分和合并为以下内容来加快处理速度:

split -n 10 -d yourFile.tsv yourFile.tsv.

这将生成yourFile.tsv.00, ..., yourFile.tsv.09

因此,整个代码类似于下面的代码块:

`split -n 10 -d yourFile.tsv yourFile.tsv.`
@yourFiles = `ls yourFile.tsv.*`;
foreach $file (@yourFiles) {
      `grep -P -v "\t\s*$" $file | cut -f2,3,12-18,25-28,31 > $file.filtered &`;
}
`cat yourFile.*.filtered > final.output.tsv`