基于列值拆分大型csv文本文件

use*_*171 10 csv text split large-data

我有CSV文件,其中有多列已排序.例如,我可能有这样的行:

19980102,,PLXS,10032,Q,A,,,15.12500,15.00000,15.12500,2
19980105,,PLXS,10032,Q,A,,,14.93750,14.75000,14.93750,2
19980106,,PLXS,10032,Q,A,,,14.56250,14.56250,14.87500,2
20111222,,PCP,63830,N,A,,,164.07001,164.09000,164.12000,1
20111223,,PCP,63830,N,A,,,164.53000,164.53000,164.55000,1
20111227,,PCP,63830,N,A,,,165.69000,165.61000,165.64000,1
Run Code Online (Sandbox Code Playgroud)

我想根据第3列分割文件,例如将PLXS和PCP条目放入他们自己的文件PLXS.csv和PCP.csv中.由于文件恰好是预先排序的,因此所有PLXS条目都在PCP条目之前,依此类推.

我通常最终在C++中做这样的事情,因为那是我最熟悉的语言,但在这种情况下,我的输入CSV文件是几千兆字节,太大而无法加载到C++的内存中.

有人可以说明如何实现这一目标吗?Perl/Python/php/bash解决方案都可以,他们只需要能够处理大文件而无需过多的内存使用.

Sea*_*ers 32

这里有一个老式的学校班轮(只需替换>>with >来截断每次运行的输出文件):

awk -F, '{print >> ($3".csv")}' input.csv
Run Code Online (Sandbox Code Playgroud)

由于受欢迎的需求(以及我刚才的痒),我还编写了一个版本,将标题行复制到所有文件:

awk -F, 'NR==1 {h=$0; next} {f=$3".csv"} !($3 in p) {p[$3]; print h > f} {print >> f}' input.csv
Run Code Online (Sandbox Code Playgroud)

但你可以从这开始,并完成第一个awk:

HDR=$(head -1 input.csv); for fn in $(tail -n+2 input.csv | cut -f3 -d, | sort -u); do echo $HDR > $fn.csv; done
Run Code Online (Sandbox Code Playgroud)

大多数现代系统都包含awk二进制文件,但是如果你没有它,你可以在Gawk for Windows上找到一个exe 文件


Mik*_*hon 1

如果您最了解 C++,那就没问题。无论如何,您为什么要尝试将整个文件加载到内存中?

由于输出取决于正在读取的列,因此您可以轻松地存储输出文件的缓冲区,并在处理时将记录填充到适当的文件中,并进行清理以保持内存占用相对较小。

当需要从数据库中提取大量数据时,我会这样做(尽管是在java中)。这些记录被推送到文件缓冲流中,并且内存中的所有内容都被清除,因此程序的占用空间永远不会超出其最初开始时的大小。

凭我的感觉伪代码:

  1. 创建一个列表来保存输出文件缓冲区
  2. 打开文件流并开始一次一行读取内容
  3. 我们是否遇到过具有其内容类型的打开文件流的记录?
    • 是的 -
      • 获取存储的文件流
      • 将记录存储到该文件中
      • 冲洗流
    • 不 -
      • 创建一个流并将其保存到我们的流列表中
      • 将记录存储在流上
      • 冲洗流
  4. 重复冲洗...

基本上继续这个处理,直到我们到达文件的末尾。

由于我们只存储指向流的指针,并且一旦写入流,我们就会刷新,除了输入文件中的一条记录之外,我们不会在应用程序的内存中保留任何驻留内容。因此,占地面积保持在可控范围内。

  • +1:C++ 不是问题。将整个文件加载到内存中是问题所在。 (3认同)