我想在 Unix 中将 TSV 文件的第 4 列截断为给定长度。文件有几百万条记录,大小为 8GB。
我正在尝试这个,但它似乎有点慢。
awk -F"\t" '{s=substr($4,0,256); print $1"\t"$2"\t"$3"\t"s"\t"$5"\t"$6"\t"$7}' file > newFile
有没有更快的替代品?
谢谢
我想在 Unix 中将 TSV 文件的第 4 列截断为给定长度。文件有几百万条记录,大小为 8GB。
我正在尝试这个,但它似乎有点慢。
awk -F"\t" '{s=substr($4,0,256); print $1"\t"$2"\t"$3"\t"s"\t"$5"\t"$6"\t"$7}' file > newFile
有没有更快的替代品?
谢谢
我假设您的文件在字段之间只有一个空格字符,并且在行首没有空格。如果这是错误的,则可以对其进行增强。否则,这应该工作:
sed 's/^\([^ ]* [^ ]* [^ ]* [^ ]\{1,256\}\)[^ ]* /\1 /'
我实际上没有用 256 个字符长的数据测试它(我用它测试过,但\{1,2\}
我不知道它的速度与awk
.并使用{1,256}
.
您的命令可以写得更好一些(假设您正在重新构建记录),这可能会提高一些性能:
awk 'BEGIN { FS=OFS="\t" } { $4 = substr($4,0,256) }' file > newFile
如果您可以访问多核机器(您可能会这样做),则可以使用GNU 并行。您可能想要改变您使用的核心数量(我在这里设置了 4 个)和馈入的块大小awk
(我已经将其设置为 2 兆字节)......
< file parallel -j 4 --pipe --block 2M -q awk 'BEGIN { FS=OFS="\t" } { $4 = substr($4,0,2) }' > newFile
这是我在我的系统上使用具有 1 亿行和 2M 块大小的 2.7G 文件进行的一些测试:
time awk 'BEGIN { FS=OFS="\t" } { $4 = substr($4,0,2) }' file >/dev/null
结果:
real 1m59.313s
user 1m57.120s
sys 0m2.190s
一个核心:
time < file parallel -j 1 --pipe --block 2M -q awk 'BEGIN { FS=OFS="\t" } { $4 = substr($4,0,2) }' >/dev/null
结果:
real 2m28.270s
user 4m3.070s
sys 0m41.560s
有四个核心:
time < file parallel -j 4 --pipe --block 2M -q awk 'BEGIN { FS=OFS="\t" } { $4 = substr($4,0,2) }' >/dev/null
结果:
real 0m54.329s
user 2m41.550s
sys 0m31.460s
有十二个核心:
time < file parallel -j 12 --pipe --block 2M -q awk 'BEGIN { FS=OFS="\t" } { $4 = substr($4,0,2) }' >/dev/null
结果:
real 0m36.581s
user 2m24.370s
sys 0m32.230s
如果 Scott 或 Steve 的解决方案仍然太慢,可能是时候打破 C. Run as ./a.out < file > newFile
. 首先测试一个带有一些长字段的小文件;我不是 100% 确定我的数学是正确的。
#include <stdio.h>
int
main(void)
{
int field = 1;
int character = 0;
int c;
while ((c = getchar()) != EOF)
{
switch (c)
{
case '\n':
field = 1;
character = 0;
break;
case '\t':
character = 0;
field++;
break;
default:
character++;
break;
}
if (field != 4 || character < 256)
putchar(c);
}
if (ferror(stdout) || fflush(stdout) || fclose(stdout))
{
perror("write");
return 1;
}
return 0;
}