1

我想在 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

有没有更快的替代品?

谢谢

4

3 回答 3

2

我假设您的文件在字段之间只有一个空格字符,并且在行首没有空格。如果这是错误的,则可以对其进行增强。否则,这应该工作:

sed 's/^\([^ ]* [^ ]* [^ ]* [^ ]\{1,256\}\)[^ ]* /\1 /'

我实际上没有用 256 个字符长的数据测试它(我用它测试过,但\{1,2\}我不知道它的速度与awk.并使用{1,256}.

于 2013-01-09T00:08:42.953 回答
2

您的命令可以写得更好一些(假设您正在重新构建记录),这可能会提高一些性能:

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
于 2013-01-08T23:54:50.557 回答
2

如果 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;
}
于 2013-01-09T00:32:33.637 回答