【问题标题】:Split CSV files into smaller files but keeping the headers?将 CSV 文件拆分为较小的文件但保留标题?
【发布时间】:2018-12-27 11:48:54
【问题描述】:

我有一个巨大的 CSV 文件,1m 行。我想知道是否有办法将此文件拆分为较小的文件,但保留所有文件的第一行(CSV 标题)。

似乎split 非常快但也非常有限。您不能为文件名添加后缀,例如 .csv

split -l11000 products.csv file_

bash 中是否有有效的方法来完成这项任务?单行命令会很棒。

【问题讨论】:

  • 嗯。我忘记了我的黄金狂欢让我有能力关闭一个问题。并不是真的要发生这种情况。我知道其他读者仍然可以回复。下面的答案看起来很不错。把它放在一个shell脚本中,然后你就拥有了单行。祝你好运..
  • 投票重新开放,因为问题不是重复的。在所有拆分文件中保留第一行有一个特定的区别。
  • 最好的工具是xsv:stackoverflow.com/a/68585985/8079808

标签: bash csv awk


【解决方案1】:

这个问题的答案是是的,这可以通过 AWK 实现。

我们的想法是记住标题并以filename.00001.csv 形式的文件名打印所有其余部分:

awk -v l=11000 '(NR==1){header=$0;next}
                (NR%l==2) {
                   close(file); 
                   file=sprintf("%s.%0.5d.csv",FILENAME,++c)
                   sub(/csv[.]/,"",file)
                   print header > file
                }
                {print > file}' file.csv

这通过以下方式工作:

  • (NR==1){header=$0;next}:如果记录/行是第一行,则将该行保存为标题
  • (NR%l==2){...}: 每次我们写l=11000 记录/行时,我们都需要开始写入一个新文件。每次记录/行号的模数达到 2 时都会发生这种情况。这是在 2、2+l、2+2l、2+3l 等行上。发现我们这样做:
    • close(file):关闭你刚刚写的文件。
    • file=sprintf("%s.%0.5d.csv",FILENAME,++c); sub(/csv[.]/,"",file): 将新文件名定义为FILENAME.00XXX.csv
    • print header > file:打开文件并将标题写入该文件。
  • {print > file}:将条目写入文件。

注意:如果您不关心文件名,可以使用以下较短的版本:

awk -v m=100 '
    (NR==1){h=$0;next}
    (NR%m==2) { close(f); f=sprintf("%s.%0.5d",FILENAME,++c); print h > f }
    {print > f}' file.csv

【讨论】:

  • 谢谢@kvantour,这在包含 100 万行的文件上运行得非常好,而且非常快 5 秒。比我的 php 代码好多了 :) 干杯。
  • 我正在尝试了解有关 AWK 的更多信息。我知道变量是动态的,但是当您尝试关闭 undefined 时,close(file) 如何不会引发异常?
  • @JonnyWaffles close() 如果给出的参数不代表通过重定向打开的文件、管道或协同进程,则不会执行任何操作。在这种情况下,它返回一个负值,表示错误。
  • 感谢您的帮助。我没有在我找到的文档中看到无声传递行为 gnu.org/software/gawk/manual/html_node/I_002fO-Functions.html。您是否有可以推荐的特定资源来了解 AWK?我正在尝试编写一个类似的脚本,通过 stdout 将文件通过管道传输到 s3 cli,因此我不会将不必要的大文件写入磁盘,但我不知道如何缓冲逻辑分区文件并调用 s3 cp - 对于每个一。
  • 这是我见过的 最好的 awk 答案 - 写得很清楚,解释清楚,很好。希望我能投 2 票。
【解决方案2】:

使用 GNU split 拆分 file.csv

export inputPrefix='file' parts=16 && split --verbose -d -n l/${parts} --additional-suffix=.csv --filter='([ "$FILE" != "${inputPrefix}.00.csv" ] && head -1 "${inputPrefix}.csv" ; cat) > "$FILE"' "${inputPrefix}.csv" "${inputPrefix}."

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 2020-06-22
    • 1970-01-01
    • 2018-11-09
    • 1970-01-01
    • 2013-07-31
    相关资源
    最近更新 更多