如何按初始列(带标题)拆分 CSV 文件?

如何按初始列(带标题)拆分 CSV 文件?

这是另外两个问题的组合(如何按每行前缀分割文件如何根据列拆分文件,包括标题)。我想从以下内容开始input.csv

id,first,second,third
1,a,b,c
333,b,b,b
1,d,e,f
2,d,e,f
1,c,d,e
333,a,a,a
[more lines in the same format]

到此内容1.csv

id,first,second,third
1,a,b,c
1,d,e,f
1,c,d,e

,此内容在2.csv

id,first,second,third
2,d,e,f

,以及此内容333.csv

id,first,second,third
333,b,b,b
333,a,a,a

, 那是:

  1. 将所有 ID 为进入N.csv
  2. 保持行的顺序和原来一样。
  3. 包含标题来自所有输出文件中的原始文件。

这也必须非常快,所以while read循环是不是要去砍它。

答案1

这个 GNU awk 命令可以解决这个问题:

awk -F ',' 'NR==1{h=$0; next};!seen[$1]++{f=$1".csv"; print h > f};{f=$1".csv"; print >> f; close(f)}' input.csv

警告:如果第一个字段中有转义逗号,这将不起作用。其他字段中的逗号应该可以正常工作。

解释:

  • -F ','(字段分隔符)确保$1等引用 CSV 列而不是空格分隔的值。
  • NR==1{h=$0; next}NR==1通过将完整的标题行存储在变量h( h=$0) 中并跳过该行 ( ),特殊对待第一行 ( ) next
  • !seen[$1]++{f=$1".csv"; print h > f}通过将后跟存储到文件名变量并将标头保存到该文件 ( ) 来处理任何$1特殊 ( )的第一次出现。!seen[$1]$1.csvfprint h > f
  • {f=$1".csv"; print >> f; close(f)}将当前行添加到文件 ( print >> f) 并关闭文件描述符 ( close(f)) 以避免在处理完具有特定 ID 的所有行后保留它。

奖励:如果您替换$1为另一个字段,它应该执行您期望的操作:为该列中的每个唯一值创建一个文件,其中包含给定列中包含该值的行。

答案2

(很抱歉用另一个答案向大家发送垃圾邮件)对于许多情况,所提供的优雅的 awk 版本是完美的。但除了俏皮话之外还有生活——我们常常需要更多:

  • 添加额外的代码来处理复杂的 csv 文件;
  • 添加额外的标准化、重新格式化、处理步骤。

在下面的框架中,我们使用 CSV 文件的解析器。这次我们避免使用一整型,甚至严格声明变量!

#!/usr/bin/perl

use strict;
use Parse::CSV;
my %dict=();

my $c = Parse::CSV->new(file => 'a1.csv');

while ( my $row = $c->fetch ) {                    ## for all records
   $dict{$row->[0]} .=   join(" :: ",@$row)."\n";  ## process and save
}

for my $k (keys %dict){                            ## create the cvs files
   open(F,">","$k.cvs") or die;
   print F $dict{$k};
   close F;
}
  • 主要优点是我们可以处理更复杂的csv文件;这次 csv 输入可以包含带“;”的字符串,可以包含多行字段(csv 规范很复杂!):
 1111,2,3
 "3,3,3",a,"b, c, and d"
 "a more, complex
        multiline record",3,4
  • 为了举例说明处理步骤,字段分隔符更改为“ :: ”
  • 为了举例说明额外的步骤,我们添加了一些优化:由于我们使用了字典缓存,因此该脚本的运行速度比我的其他解决方案快 100 倍。

答案3

这不是一个答案,而只是 IObO 优秀答案的避免滚动变体......

awk -F, 'NR==1{h=$0; next} {print seen[$1]++ ? $0 : h "\n" $0 >$1 ".csv"}'

答案4

old school version using only pipes and no awk:

警告:它的运行速度平均比上述awk解决方案慢,速度是输入文件中键数量的一个因素

cut -d , -f 1 input.csv | fgrep -v id | sort | uniq | xargs -n1 sh -c '(head -n1 input.csv && egrep "^${0}," input.csv) > ${0}.csv'

它的作用是:

  • cut -d , -f 1 input.csv按字符分割文件的每一行,并抓取第一列 ( -f 1) 以仅保留键
  • fgrep -v id跳过标题
  • sort | uniq排序并仅保留每个键之一
  • xargs -n1 sh -c '<sub shell>'对于每个键执行一个子 shell
  • head -n1 input.csv子 shell 的第一部分获取输入文件的标头
  • 然后egrep "^${0}," input.csv抓取与键匹配的行,它可能不明显,但这是每行循环,这就是它很慢的原因
  • 最后> ${0}.csv将输出写入由密钥命名的文件中

相关内容