我的查询的详细信息如下:
- 我有一个非常大的 TSV(Tab Sep. Value)文件(其中大于 30 GB)。
- 我想从此文件中提取某些不以空最后字段结尾的行。由于这是一个 TSV 文件,因此那些不以
\t\n
结尾的行,这是一个简单的测试,不是这个问题的主题。这将立即删除大约 75% 的线路,从而减少工作量。 - 然后我想从剩余的行中提取一小部分字段。这些字段不连续,但数量很少(例如,总共三十多个字段中的七个)。例如,假设字段
2,3,12-18,25-28,31
。 - 我要提取的行非常长,大多数长达 1,000 个字符,因为它们包含大量制表符分隔字段。
显然,一个选择是使用以下简单的代码,我已尝试对其进行良好的格式化并包含注释以显示我的推理:
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 会导致额外的解析(超出我需要的最后一个字段)并生成我也不需要的完整字段数组。我认为不创建数组会更有效,而是解析每一行以查找选项卡并计算字段索引,在途中创建输出行,并在我需要的最后一个字段处停止。
我的评估是否正确,或者只是进行简单的分割
,然后对包含感兴趣字段的切片进行连接
,这是最好的方法从性能角度来看?
更新:不幸的是,没有人提到使用GNU cut
进行分割并将结果传输到Perl中进行其余处理的可能性。这可能是最高效的方法,无需编写大量自定义 (C) 代码来执行此操作,也无需借助自定义行解析(也在 C 中)进行基于大块的读取。
最佳答案
您可以使用其 limit 参数告诉 split 何时停止:
my @fields=split(/\t/,$_,33);
(指定比您实际需要的字段数多一个的字段,因为它生成的最后一个字段将包含该行的剩余部分。)
关于perl - 当只需要字段的子集时,分割长行的高性能方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56670693/