【问题标题】:How to break large ".csv" file into small files based on multiple conditions?如何根据多个条件将大“.csv”文件分解为小文件?
【发布时间】:2015-11-13 21:13:55
【问题描述】:

我有大的 .csv 文件(~40MB),我想通过几个条件将它们分成更小的文件,并根据数据相应地命名它们:

  1. 按第 3 列的内容分隔文件,
  2. 将第 1 点的输出按内容第 4 列分开,

这是棘手的部分:

  1. 由 2 个先前操作创建的输出检查第 11 列中是否有任何数据,如果有,则根据内容将此数据分开,然后按第 17 列的内容分开 -> 然后保存输出 /或/与/
  2. 如果第 11 列中没有数据,请检查第 15 列并相应地分开。接下来检查第 17 列并将此数据按第 17 列分隔 -> 保存输出。

我在 VBA 中有类似的东西,但它对于大文件来说太慢了,而且 Excel 有时会崩溃。像这样的多个文件需要很长时间才能手动删除它们,然后将它们放入 vba 中。

这么多条件下可以剪切文件吗?

提前感谢您的帮助。

例如: (标题是列的#)

1       2   3   4   11  15  17
Date        Time    COUNTRY CITY    CHECK   TEST    TEST2
2015-08-20  11:54   ENGLAND ABINGDON        1       1
2015-08-21  12:54   ENGLAND BATLEY          2       5
2015-08-22  13:54   ENGLAND FROME           2       6
2015-08-23  14:54   ENGLAND FROME   2       1
2015-08-24  15:54   USA CALIFORNIA          4       8
2015-08-25  16:54   USA CONNECTICUT         4       9
2015-08-26  17:54   USA DELAWARE    1               8
2015-08-27  18:54   GERMANY SAXONY          6       9
2015-08-28  19:54   GERMANY SAXONY          6       10
2015-08-27  18:54   GERMANY SAXONY          4       11
2015-08-28  19:54   GERMANY SAXONY          4       14
2015-08-29  20:54   GERMANY HESSE                   8
2015-08-29  20:54   GERMANY HESSE   1               8

File1                       
2015-08-20  11:54   ENGLAND ABINGDON        1       1

File2                       
2015-08-21  12:54   ENGLAND BATLEY          2       5

File3                       
2015-08-22  13:54   ENGLAND FROME           2       6

File4                       
2015-08-23  14:54   ENGLAND FROME   2               1

File5                       
2015-08-24  15:54   USA CALIFORNIA          4       8

File6                       
2015-08-25  16:54   USA CONNECTICUT         4       9

File7                       
2015-08-26  17:54   USA DELAWARE    1               8

File8                       
2015-08-27  18:54   GERMANY SAXONY          4       9
2015-08-28  19:54   GERMANY SAXONY          4       10

File9                       
2015-08-27  18:54   GERMANY SAXONY          6       11
2015-08-28  19:54   GERMANY SAXONY          6       14

File10                      
2015-08-29  20:54   GERMANY HESSE                   8

File11                      
2015-08-29  20:54   GERMANY HESSE   1               8

【问题讨论】:

  • 也许可以向我们展示 10-20 行的混合数据以及处理后的外观。
  • @MarkSetchell 我在问题描述中添加了“示例”。
  • 你有没有在 bash 中尝试过任何东西?你的分隔符是什么?制表符?
  • @carlspring 我是 bash 的新手(一些真正的样本切割等)。我在 VBA 方面更高级,我的分隔符是 TAB。

标签: linux excel bash csv command-line


【解决方案1】:

您的数据无处不在!它不在您描述的列中,也不是制表符分隔的。你没有让生活变得轻松!

用你的真实数据试试这个awk,看看它是否会生成一个你可以使用的输出文件名。

awk -F'\t' '{
    f=$3 "_" $4                # filename = field3 _ field4
    if(length($11)){           # if f11 not null
      f=f "_A_" $11 "_" $17    #    filename = filename _A_ field11 _ field17
    }else{                     # else
      f=f "_B_" $15 "_" $17    #    filename = filename _B_ field15 _ field17
    }
    print f}' file.csv

你应该得到这样的东西

ENGLAND_ABINGDON_A_3_1
ENGLAND_ABINGDON_A_4_2
GENRMANY_SAXONY_B_5_3

基本上它使用awk 并告诉它您的字段分隔符是制表符。然后它会查看每一行,并通过查看您描述的字段在变量f 中构建一个输出文件名。

如果这将您的数据按您的意思分开,您实际上可以通过简单地更改最后一行,将当前行输出到具有相应名称的文件:

awk -F'\t' '{
    f=$3 "_" $4                # filename = field3 _ field4
    if(length($11)){           # if f11 not null
      f=f "_A_" $11 "_" $17    #    filename = filename _A_ field11 _ field17
    }else{                     # else
      f=f "_B_" $15 "_" $17    #    filename = filename _B_ field15 _ field17
    }
    print > f}' file.csv

基本上它会打印文件,而不是在您更改时打印其名称

print f

print > f

制定标题

如果你想在每个输出文件上都有一个标题,我们需要加倍努力......

首先,我们需要从原始文件中保存标题,所以如果我们假设这是第 1 条记录,我们会这样做

awk -F'\t' '
    NR==1 {header=$0}           # save first line as header
    {f=$3 "_" $4                # filename = field3 _ field4
    ...
    ... as before

现在我们需要在开始写入新文件时输出一个标题行,即"fun",因为我们只是为每一行动态创建输出文件名!所以,我们需要“记住”我们写入了哪些文件,然后只在我们写入新文件时发出一个标题。我这里没有你的一套像样的数据,所以我猜这个!

awk -F'\t' '
    NR==1 {header=$0}          # save first line as header
    {f=$3 "_" $4               # filename = field3 _ field4
    if(length($11)){           # if f11 not null
      f=f "_A_" $11 "_" $17    #    filename = filename _A_ field11 _ field17
    }else{                     # else
      f=f "_B_" $15 "_" $17    #    filename = filename _B_ field15 _ field17
    }
    # Emit header if first write to this filename
    if(!(f in fileswritten)){
       fileswritten[f]++         # note that we have written to this file
       print header > f          # emit header
    }
    print > f}' file.csv

【讨论】:

  • 谢谢。我会在今天的空闲时间检查一下。
  • 马克,我部署了该脚本及其工作,但告诉我,有没有办法将原始文件的标题添加到每个文件?我的意思是列的名称?
  • 我在最后添加了一些代码来尝试获取标题,但未经测试。它应该足够接近,但您可以确定它是否有任何错误。
【解决方案2】:

此答案不完整,但大致说明了您需要做什么:

#!/bin/bash

# Get list of countries:
countries=`cat file1.csv | cut -f 3 -d$'\t'| grep -v 3 | grep -v COUNTRY | uniq`

for country in ${countries}; do
    # Get list of cities per country:
    cities=`cat file1.csv | grep ${country} | cut -f 4 -d$'\t' | uniq`

    # Get data per country:
    cat file1.csv | grep ${country} > file1-${country}.csv

    # Get data per city per country:
    for city in ${cities}; do
    echo ${country}:${city}

    cat file1.csv | grep ${country} | grep ${city} > file1-${country}-${city}.csv
    done

    # Created output by 2 previous operations check if there is any data in 11th column,
    # if yes then separate this data accordingly to content and after that separate that
    # by content of 17th column -> then save outputs /OR / AND /
    # Column 11 is at position 5 in your data.
    check=`cat file1.csv | grep ${country} | cut -f 5 -d$'\t' | uniq`
    for check in ${checks}; do
        echo ${country}:${city}:${check}

        cat file1.csv | grep ${country} | grep ${city} > file1-${country}-${city}-${check}.csv

        # TODO: Further split this, I assume you get the drift by now.
    done

    # If there is no data in column 11 check column 15th and separate accordingly.
    # Next check 17 column and separate this data by 17th column -> save outputs.
    # TODO: Further split this, I assume you get the drift by now.

done

【讨论】:

  • 感谢您的回答。今天晚些时候我需要更深入地研究这一点。顺便说一句,如果编号列之间有数据,我假设它们也会被复制?
  • 试试这个脚本,你会看到的。数据被复制,是的。如果您使用它,它非常简单。
  • carlspring,我正在检查那个脚本,到目前为止它的工作,需要进一步的模组,但我会问马克,有没有办法为每个文件添加标题?提前致谢。
【解决方案3】:

我建议写一个小脚本,使用java库CSVFormat:

private static final String[] FILE_HEADER_MAPPING = {"Date", "Time" ,"COUNTRY", .... };
csvFileParser = new CSVParser(fileReader, csvFileFormat);
        List<CSVRecord> csvRecords = csvFileParser.getRecords();

然后要访问第 11 列,您必须这样做

 for (int i = 1; i < csvRecords.size(); i++) {
    boolean publishAccount = true;
    CSVRecord record = csvRecords.get(i);
    /// here how to access
    record.get("Fiel column 11");  
 }

【讨论】:

  • 感谢您的回答,我也需要检查该脚本并尝试哪个运行得更快。老实说,我不知道你的脚本是如何工作的。
  • 如果你创建了一个简单的 java 程序,你可以在你的主类中编写它,然后通过你的 unix 脚本简单地调用它。我喜欢这种方式,因为它易于阅读和修改。您只需要在其中包含 csv 库。或者,在 java 中,您可以在字符串中使用 split 方法
猜你喜欢
  • 2019-06-14
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
  • 2019-09-23
  • 1970-01-01
  • 2015-09-03
  • 2022-01-12
  • 1970-01-01
相关资源
最近更新 更多