【问题标题】:Looking for a efficient split of file寻找有效的文件分割
【发布时间】:2012-09-12 03:52:40
【问题描述】:

假设这是我的文件:

$ cat file.txt 
A:1:i
B:2:ii
X:9:iv

使用这样的 for 循环,我可以分别打印所有字段并重定向到子文件

$ for i in $(seq 1 3); do echo $i; awk -F ":" -v FL=$i '{print $FL}' file.txt > $i.out; done

这样:

$ cat 1.out 
A
B
X

$ cat 2.out 
1
2
9

$ cat 3.out 
i
ii
iv

问题: 我必须在近 70 列和近 10 GB 的文件大小上执行此操作。它有效,但速度很慢。 任何人都可以建议一个更好/有效的拆分来处理这个大数据集。谢谢。

$ for i in $(seq 1 70); do echo $i; awk -F ":" -v FL=$i '{print $FL}' *.data > $i.out; done

【问题讨论】:

  • 您是否仅限于 shell 脚本?
  • 感谢 jedwards。连python都可以。

标签: shell unix command-line awk


【解决方案1】:

考虑到您要执行的操作,这应该相当快:

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

【讨论】:

  • 简洁优雅。做得很好。唯一要注意的是,这在 gawk 中有效,但在 BSD awk 中无效(FreeBSD、OSX 等中的默认 awk)。我相信 awk 不喜欢您的重定向目标。不过,它适用于您使用out=i".out"; print $i &gt; out;
【解决方案2】:

Python 版本

#!/bin/env python

with open('file.txt', 'r') as ih:
    while True:
        line = ih.readline()
        if line == '': break
        for i,element in enumerate(line.strip().split(':')):
            outfile = "%d.out" % (i+1)
            with open(outfile, 'a') as oh:
                oh.write("%s\n" % element)

这可能会快一点,因为它只遍历原始文件一次。请注意,可以通过保持输出文件打开来进一步优化它(实际上,我关闭每个文件并在每次写入时重新打开它们)。

编辑

例如:

#!/bin/env python

handles = dict()

with open('file.txt', 'r') as ih:
    while True:
        line = ih.readline()
        if line == '': break
        for i,element in enumerate(line.strip().split(':')):
            outfile = "%d.out" % (i+1)

            if outfile not in handles:
                handles[outfile] = open(outfile, 'a');

            handles[outfile].write("%s\n" % element)

for k in handles:
    handles[k].close()

这会使句柄在执行期间保持打开状态,然后在继续/结束之前将它们全部关闭。

【讨论】:

  • 像这样打开和关闭文件的成本很高,尤其是在网络文件系统上。
  • @epsalon,我同意——我编辑的答案包括一个使文件保持打开状态的版本。
【解决方案3】:

在 perl 中,您可以:

#!/usr/bin/perl -w
my $n = 3;
my @FILES;
for my $i (1..$n) {
  my $f;
  open ($f, "> $i.out") or die;
  push @FILES, $f;
}
while (<>) {
  chomp;
  @a = split(/:/);
  for my $i (0..$#a) {
    print $FILES[$i] $a[$i],"\n";
  }
}
close($f) for $f in @FILES;

【讨论】:

    【解决方案4】:

    如果您知道有三列,则使用 coreutils:

    < file.txt tee >(cut -d: -f1 > 1.out) >(cut -d: -f2 > 2.out) >(cut -d: -f3 > 3.out) > /dev/null
    

    为了使其更通用,这里有一种自动化命令行生成的方法:

    # Determine number of fields and generate tee argument
    arg=""
    i=1
    while read; do 
      arg="$arg >(cut -d: -f$i > $((i++)).out)"
    done < <(head -n1 file.txt | tr ':' '\n')
    

    arg 现在是:

    >(cut -d: -f1 > 1.out) >(cut -d: -f2 > 2.out) >(cut -d: -f3 > 3.out)
    

    保存到脚本文件:

    echo "< file.txt tee $arg > /dev/null" > script
    

    并执行:

    . ./script
    

    【讨论】:

      【解决方案5】:

      这是一个 bash 脚本,它使用了我不经常看到的功能:要求 bash 为文件分配文件描述符并将描述符存储在变量中:

      # Read the first line to get a count of the columns
      IFS=: read -a columns < file.txt
      
      # Open an output file for each column, saving the file descriptor in an array
      for c in "${columns[@]}"; do
          exec {a}>$((++i)).txt
          fds+=( $a )
      done
      
      # Iterate through the iput, writing each column to the file opened for it
      while IFS=: read -a fields; do
          for f in "${fields[@]}"; do
              printf "$f\n" >&${fds[++i]}
          done
      done < file.txt
      
      # Close the file descriptors
      for fd in "${fds[@]}"; do
          exec {fd}>&-
      done
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-09
        • 2011-08-22
        • 1970-01-01
        • 2011-02-04
        • 1970-01-01
        相关资源
        最近更新 更多