将文本文件中的数据列保存为单独的文件



我正在寻找一种方法来采取一个文本文件,有由制表符分隔的数据列:

file.txt

abcd    abcd    abcd
efgh    efgh    efgh
ijkl    ijkl    ijkl
mnop    mnop    mnop
qrst    qrst    qrst

使用awk,我想将每列数据保存为自己的文本文件,使用数字作为文件名。

但问题是没有办法预测它们将包含的文本的列数

我唯一知道的是,列之间将用制表符分隔。

,

awk '{ print $1 }' file  

将打印第一列

:

awk '{ print $2 }' file

将打印第二列

然而,我希望保存每列作为自己的文件。

列数可以是100位以内的任意数

所有输出文件同时打开

一个GNU awk的想法:

awk '{for (i=1;i<=NF;i++) print $i > i".out"}' file

指出:

  • 将为每个输出文件打开一个文件描述符,并保持打开状态
  • 许多awk实现对一次可以打开的文件数量有限制;打开和关闭文件非常耗时,因此从性能角度考虑,您需要限制打开和关闭操作的次数
  • GNU awk对一次可以打开多少文件有相当高的限制
  • 如果你有GNU awk,你收到一个错误,说明太多的打开文件描述符,然后让我们知道,我们可以看看另一个想法(例如:运行一个单独的awk为每一组N列;使用内存解决方案(假设整个文件都可以放入内存中)
  • 你提到列由tab spaces分隔;我不知道你是什么意思…列由多个制表符和空格分隔?列由制表符或空格分隔?)这个答案使用awk's默认字段分隔符'空白'(多个空格/制表符视为单个分隔符);如果您的字段由制表符分隔,但在字段中包含空格,则将awk '{for ...更改为awk -F't' '{for ...

内存;一次打开一个输出文件;香草awk

假设输入文件可以装入内存:

一个应该适用于所有awk口味的想法:

awk '
    { for (i=1;i<=NF;i++)
          cols[i]=cols[i] (FNR==1 ? "" : ORS) $i
    }
END { for (i=1;i<=NF;i++) {
          outfile= i ".out"
          print cols[i] > outfile
          close(outfile)
      }
    }
' file

内存;一次打开一个输出文件;GNU awk

另一个使用GNU awk(用于多维数组支持)的内存解决方案:

awk '
    { for(i=1;i<=NF;i++)
         cols[i][FNR] = $i 
    }
END { for (i=1;i<=NF;i++) {
          outfile= i ".out"
          for (j=1;j<=FNR;j++)
              print cols[i][j] > outfile
          close(outfile)
      }
    }
' file

所有这三个答案产生:

$ head ?.out
==> 1.out <==
abcd
efgh
ijkl
mnop
qrst
==> 2.out <==
abcd
efgh
ijkl
mnop
qrst
==> 3.out <==
abcd
efgh
ijkl
mnop
qrst

绩效考核

设置:

# create a file with 5000 lines and 500 columns; ~19.5 MBytes
awk '
BEGIN { for (i=1;i<=5000;i++) {
            printf "%s", "col_1"
            for (j=2;j<=500;j++)
                printf "t%s", "col_" j
            print ""
        }
      }
' > multi_column.txt

250万个打开/关闭操作

运行打开/关闭500个输出文件的两个答案中的任意一个,对于5000个输入行,(即5000 x 500 = 2.5 million打开/关闭操作):

  • 在2分钟后被杀死,处理了800行
  • 外推:~12.5分钟处理5000行
  • 时间将(显然)根据硬件而变化(例如,Ed Morton报告他的答案需要10分钟)在他的笔记本电脑上)

所有(500)个输出文件同时打开

运行第一个答案(如上):

  • 10秒生成500个文件,每个文件5000行
  • 即使我们必须限制自己,比如一次处理20列……我们可以对输入文件进行25次传递,并且仍然在<7分钟(可以通过并行运行一些awk会话来进一步减少时间)

内存;一次打开一个输出文件;香草awk

运行第二个答案(上面)

  • 6秒生成500个文件,每个文件5000行

内存;一次打开一个输出文件;GNU awk

运行第三个答案(如上):

  • 3秒生成500个文件,每个文件5000行
  • 先前的内存回答较慢,因为需要时间"查找并追加"新字段到长度不断增加的数组条目的末尾(cols[i]=cols[i] (FNR==1 ? "" : ORS) $i)

无论您的输入有多少列,都可以移植到所有的awks:

awk -F't' '{
    for (i=1; i<=NF; i++) {
        out = $i ".out"
        if ( !seen[out]++ ) {
            printf "" > out
        }
        print $i >> out
        close(out)
    }
}' file

你可以这样做:

awk 'NR==FNR{max=NF>max ? NF : max; next} 
{for(i=1; i<=max; i++) {
    fn=sprintf("%s.col", i)
    print $i >> fn
    close(fn)
    }
}' file file 

如果你的列宽度是一致的,你可以做一次:

awk 'FNR==1{max=NF}
{for(i=1; i<=max; i++) {
    fn=sprintf("%s.col", i)
    print $i >> fn
    close(fn)
    }
}' file

使用示例创建以下文件:

$ head *.col
==> 1.col <==
abcd
efgh
ijkl
mnop
qrst
==> 2.col <==
abcd
efgh
ijkl
mnop
qrst
==> 3.col <==
abcd
efgh
ijkl
mnop
qrst

使用数组避免每行重复写入

awk '
        {
                for(i=1; i<=NF; i++){
                  # saving columns in multi-array 
                  # i = column, NR = line
                  a[i][NR] = $(i)  
                }
        }
        END{
                # iterating through array
                for (col in a){
                 joined = ""
                 # joining lines per column
                 for (line in a[col]){
                  joined = joined a[col][line] "n"
                 }
                 gsub(/n$/,"",joined)
                 # write all joined lines per column at once to col-file
                 print joined > col".out"
                }
        }
' file.txt

相关内容

  • 没有找到相关文章

最新更新