1

我有一个相当大的数据集(10K 文件,每个文件有 20K 行)。我需要交换文件和行,(给自己 20K 个文件,每个文件有 10K 行)。

我有一个解决方案,将它们全部组合到一个大表中,然后使用 cut.. 提取列。但是 cut 花费的时间太长(扫描 4GB 文件 10K 次并不是很快,即使文件位于缓存中)。

所以我在 awk 中写了一个(令人惊讶的简单)一次性:

awk '{ print >> "times/"FNR".txt" }' posns/*

这可以完成工作,但也相当慢(每个输入文件大约 10 秒)。我的猜测是它正在进行字段分离,尽管我根本不需要它。有没有办法禁用该功能以加快速度,还是我必须用另一种语言编写解决方案?

如果有帮助,虽然我更喜欢通用解决方案,但每个文件中的每一行都是格式%d %lf %lf,因此在这种情况下,行最多为 21 个字节(浮点数均小于 100,整数为 0 或 1 )。

4

4 回答 4

1

你可以尝试不同的awk。我听说 mawk 比其他 awk 快,并且 GNU awk 有一些性能改进,这意味着它可能比您使用的任何东西运行得更快。如果您将字段分隔符设置为记录分隔符,那么每行将只有一个字段,因此如果您对字段拆分是正确的问题,那么也许这会加快速度。此外,您使用了错误的重定向运算符 - 您应该使用“>”而不是“>>”并且字符串连接很慢,所以我建议只打印到编号文件,然后再重命名它们。

像这样的东西:

cd times
awk -F'\n' '{ print > FNR }' ../posns/*
for f in *
do
    mv -- "$f" "${f}.txt"
done
cd ..

您可能想先在一个虚拟目录上对其进行测试。

在这个线程中的其他评论可能会同时打开这么多文件,这就是问题所在,您可以根据文件名中的某些模式对子组执行此操作吗?例如,如果您的 posns 文件都以数字开头:

cd times
rm -f *
for ((i=0; i<=9; i++))
do
   awk -F'\n' '{ print >> FNR }' ../posns/"$i"*
   for f in *
   do
      mv -- "$f" "${f}.txt"
   done
done
cd ..

请注意,在这种情况下,您需要先对输出文件进行 zap。我确信有比这更好的文件分组方式,但您需要告诉我们是否有命名约定。

于 2012-12-07T19:59:02.997 回答
1

我不知道这是否比 awk 快,但这是一个可以完成任务的 perl 脚本:

#!/usr/bin/perl

use strict;
use warnings;

my $line=0;

foreach(@ARGV){

 open (MYINFILE, $_);
 $line=0;

 while(<MYINFILE>){
  $line++;
  open (MYOUTFILE,">>times/$line.txt");
  print MYOUTFILE $_;
  close (MYOUTFILE);
 }

}
于 2012-12-07T21:15:31.660 回答
0

这听起来像是拆分的完美工作;)

find posns -type f -exec split -l 10000 {} \;

您可以使用-a-d选项来自定义结果文件后缀。

解释:

  • find posns -type f:查找目录中的所有文件(递归)posns
  • -exec ... \;:对于找到的每个结果,执行以下命令...
  • split -l 10000 {}:{}当与-exec. split -l 10000将输入文件分成最多 10k 行的块。
于 2012-12-07T19:53:22.400 回答
0

最终我放弃了漂亮的 shell 方法,并用 C 编写了另一个版本。很遗憾,它并不漂亮,但它的速度提高了三个数量级以上(总运行时间为 43 秒,而估计为 28 小时awk 方法,给定预缓存的数据)。它需要更改 ulimit 以允许打开足够多的文件,如果您的行长于 LINE_LENGTH,它将无法正常工作。

尽管如此,它的运行速度还是比下一个最佳解决方案快 2300 倍。

如果有人偶然发现这个想要做这个任务,这个会做的。请小心并检查它是否确实有效。

    #include <stdio.h>
    #include <stdlib.h>

    #define LINE_LENGTH 1024

    int main(int argc, char* argv[]) {
            int fn;
            int ln;
            char read[LINE_LENGTH];

            int fmax=10;
            int ftot=0;
            FILE** files=malloc(fmax*sizeof(FILE*));
            char fname[255];
            printf("%d arguments\n", argc);

            printf("opening %s\n",argv[1]);
            FILE* open = fopen(argv[1],"r");

            for(ln=0;fgets(read,LINE_LENGTH,open); ln++) {
                    if(ln==fmax) {
                            printf("%d has reached %d; reallocing\n",ln,fmax);
                            fmax*=2;
                            files=realloc(files,fmax*sizeof(FILE*));
                    }
                    sprintf(fname, "times/%09d.txt",ln);
                    files[ln]=fopen(fname,"w");
                    if(files[ln]==0) {
                            fprintf(stderr,"Failed at opening file number %d\n",ln);
                            return 1;
                    }
                    fprintf(files[ln],"%s",read);
            }
            ftot=ln;
            fclose(open);

            for(fn=2;fn<argc;fn++) {
                    printf("working on file %d\n",fn);
                    open=fopen(argv[fn],"r");
                    for(ln=0;fgets(read,LINE_LENGTH,open); ln++) {
                            fprintf(files[ln],"%s",read);
                    }
                    fclose(open);
            }
            for(ln=0;ln<ftot;ln++) {
                    fclose(files[ln]);
            }
            return 0;
    }
于 2012-12-07T20:56:53.830 回答