【问题标题】:Is there a faster way to split a text file when a certain token is found?找到某个令牌时,是否有更快的方法来拆分文本文件?
【发布时间】:2014-09-11 04:38:25
【问题描述】:

我有一个 7GB 的文本文件,其中包含多行记录,这些记录由仅包含标记“$$$$”的行分隔。

我编写了一个方法来拆分它,一次解析一行,测试令牌,然后进行相应的拆分。这个想法是以循环方式将每个多行记录写入不同的输出文件。我的代码如下:

// Open all temp files for reading
int nThreads = threadData.size();
std::vector<ofstream*> ostrms(nThreads);
for (int i = 0; i < nThreads; ++i)
{
    ostrms[i] = new ofstream(threadData[i].InFileName);
    if (! ostrms[i]->is_open() )
        return(false);
}

// parse mol records into temp files in round-robin fashion
std::vector<std::string> molRecord;
std::string line;
const std::string MOL_END_OF_RECORD = "$$$$";
int curOutfileNo = 0;
while( ! strm.eof() )
{
    std::getline(strm,line);
    if (line.find(MOL_END_OF_RECORD) != std::string::npos)
    {
        for (int i = 0; i < molRecord.size(); ++i)
            *(ostrms[curOutfileNo]) << molRecord[i] << "\n";
        (*ostrms[curOutfileNo]) << line << "\n";
        curOutfileNo = (curOutfileNo+1) % nThreads;
        molRecord.clear();
    }
    else
        molRecord.push_back(line);
}

for (int i = 0; i < nThreads; ++i)
    delete ostrms[i];

非常运行缓慢(几分钟)。有更快的方法吗?

7GB 的文本文件有 245,634,858 行和 466537 条由“$$$$”分隔的唯一记录

【问题讨论】:

  • 为什么要换行?为什么不直接搜索“$$$$”?
  • @FoggyDay 因为我想将多行记录写入他们自己的文件。所以我想把我的 7Gb 分成 4 个 1.75-ish GB 的文件,里面有完整的记录。
  • 您是否对运行进行了基准测试,例如wc(1) 在你的大文件上?如果您的程序运行例如,请不要感到惊讶。比 wc 慢 10 倍 ...
  • 很遗憾 wc 和 grep -c 都花了至少一分钟
  • while( ! strm.eof() ) 已损坏 - 使用 while (std::getline(strm, line))。鉴于wc 至少需要一分钟,并且您的程序的写入和读取预计会增加一倍以上,如果输出到同一个驱动器,那么基本 I/O 操作听起来会非常接近您已经提到的“几分钟”......如果无论如何您都迫切希望提高性能,也许可以尝试使用内存映射 I/O:您可以写出由指针分隔的区域,而不需要类似于 molRecord 的任何内容。

标签: c++ text split


【解决方案1】:

如果您绝对确定您的分割线包含 完全 $$$$ 没有任何前缀或后缀字符(例如空格),您可以替换

 if (line.find(MOL_END_OF_RECORD) != std::string::npos)

 if (line == std::string(MOL_END_OF_RECORD))

但我认为这并不重要。

如果花一天时间改进编码是值得的(我相信它不是),并且假设是一个 Linux 系统,你可以小心地使用一些低级系统调用的巧妙组合,如 read(2)至少 64 KB 的缓冲区,mmap(2) 在数兆字节范围内,posix_fadvise(2)readahead(2)(在单独的线程中),...

如果您多次访问 same 文件(内容不变),您可能会考虑对其进行预处理(或预消化),例如填充一些GDBM 索引文件,或一些Sqlite(或其他)“数据库”,并让您的实际应用程序使用这些。您还可以简单地计算一些“索引”文件,其中包含每个 $$$$ 分隔符的偏移量。

正如我所评论的,您应该将time(1) 等实用程序所花费的wc(1) 视为执行时间的合理下限。我想他们可以告诉你,事实上(在你的特定系统上)程序是 I/O 绑定的。

顺便说一句,如果您的机器有多个,例如10GB RAM,您可以在运行程序之前简单地wc yourhugefilewc 进程将用您的文件数据填充文件系统 RAM 缓存。见http://www.linuxatemyram.com/

除非您解释什么是海量数据、更改的频率以及您的应用程序有什么作用,否则我们无法提供更多帮助......

您还可以购买更多 RAM 和/或一些 SSD...

【讨论】:

    猜你喜欢
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 2018-03-04
    • 1970-01-01
    • 1970-01-01
    • 2015-11-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多