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

Save columns of data from a text file as separate files

我正在寻找一种方法来获取包含由制表符分隔的数据列的文本文件:

file.txt

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

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

但问题是无法预测它们将包含的文本的列数,

我唯一知道的是列将由制表符分隔。

这样

awk '{ print  }' file  

将打印第一列

和:

awk '{ print  }' file

将打印第二列

但是我希望将每一列保存为自己的文件。

列数可以是 100 以内的任何值

你可以这样做:

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

同时打开所有输出文件

一个GNU awk想法:

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

备注:

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

in-memory;一次打开一个输出文件;香草 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

in-memory;一次打开一个输出文件; GNU awk

使用GNU awk的另一个in-memory解决方案(用于multi-dimensional数组支持):

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

所有这 3 个答案生成:

$ 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 万 open/close 次操作

运行 500 个输出文件中的每个 open/close 的 2x 个答案中的任何一个,对于 5000 个输入行中的每个,(即 5000 x 500 = 2.5 million open/close 操作):

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

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

运行 第一个回答(上):

  • 10秒生成500个文件,每个文件5000行
  • 即使我们不得不限制自己一次处理 20 列......我们可以对输入文件进行 25 次传递,并且仍然在 < 7 分钟内完成(时间可以进一步减少 运行 一些 awk 并行会议)

in-memory;一次打开一个输出文件;香草 awk

运行第二个答案(上)

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

in-memory;一次打开一个输出文件; GNU awk

运行第三个答案(上):

  • 3秒生成500个文件,每个文件5000行
  • 之前的 in-memory 答案较慢,因为 'find and append' 将新字段添加到 ever-increasing-in-length 数组条目的末尾 (cols[i]=cols[i] (FNR==1 ? "" : ORS) $i)

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

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

使用数组避免为每一行重复写入过程

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