【问题标题】:effeciently read large spreadsheet file in C++在 C++ 中有效地读取大型电子表格文件
【发布时间】:2010-06-24 14:42:26
【问题描述】:

我通常使用csv parser 中描述的方法来读取电子表格文件。然而,当读取一个包含大约 40 列和 250K 行数据的 64MB 文件时,大约需要 4 分钟。在原始方法中,使用一个CSVRow类逐行读取文件,并使用一个私有向量来存储一行中的所有数据。

注意几点:

  • 我确实保留了足够的向量容量,但没有太大帮助。
  • 我还需要在读取每一行时创建某个类的实例,但是即使代码只是读取数据而不创建任何实例,也需要很长时间。
  • 文件是制表符分隔而不是逗号分隔,但我认为这并不重要。

由于该文件中的某些列不是有用的数据,我将方法更改为拥有一个私有字符串成员来存储所有数据,然后找到第 (n-1) 个和第 n 个分隔符的位置以获得有用的数据(当然有很多有用的列)。通过这样做,我避免了一些 push_back 操作,并将时间缩短到 2 分钟多一点。但是,这对我来说仍然太长了。

这是我的问题:

  1. 有没有办法阅读这样的 电子表格文件更高效?

  2. 我应该通过缓冲区读取文件吗 而不是逐行?如果是这样,如何 通过缓冲区读取并使用 csvrow 上课?

  3. 我没有尝试过 boost tokenizer,是 效率更高?

感谢您的帮助!

【问题讨论】:

标签: c++ csv spreadsheet


【解决方案1】:

看起来你被 IO 限制了。不是逐行读取文件,而是以大约 8 MB 的块读取它。解析块读取的记录并确定块的结尾是否是部分记录。如果是,则从块中复制最后一条记录的部分并将其添加到下一个块中。重复直到文件被全部读取。这样,对于一个 64 MB 的文件,您只需发出 8 个 IO 请求。您可以尝试使用块大小来确定最佳性能与内存使用情况。

【讨论】:

    【解决方案2】:

    如果将整个数据读入内存可接受(显然是这样),那么我会这样做:

    1. 将整个文件读入 std::vector
    2. 填充一个向量 >,其中包含所有换行符和单元格数据的起始位置。这些位置表示每个单元格的开始/结束

    一些代码草图来展示这个想法:

    vector<vector<vector<char>::size_Type> > rows;
    for ( vector<char>::size_type i = 0; i < data.size(); ++i ) {
        vector<vector<char>::size_type> currentRow;
        currentRow.push_back( i );
        while ( data[i] != '\n' ) {
            if ( data[i] == ',' ) { // XXX consider comma at end of line
                currentRow.push_back( i );
            }
        }
        rows.push_back( currentRow );  
    }
    // XXX consider files which don't end in a newline
    

    因此,您知道所有换行符和所有逗号的位置,并且您可以将完整的 CSV 日期作为一个连续的内存块使用。因此,您可以像这样轻松提取单元格文本:

    // XXX error checking omitted for simplicity
    string getCellText( int row, int col )
    {
         // XXX Needs handling for last cell of a line
         const vector<char>::size_type start = rows[row][col];
         const vector<char>::size_type end = rows[row][col + 1]; 
         return string(data[start], data[end]);
    }
    

    【讨论】:

      【解决方案3】:

      This article 应该会有所帮助。

      简而言之:
      1. 使用内存映射文件或读取 4kbyte 块中的文件来访问数据。内存映射文件会更快。
      2. 尽量避免在解析循环中使用来自 stl 的 push_back、std::string 操作(如 +)和类似例程。它们很好,但是它们都使用动态分配的内存,并且动态内存分配很慢。任何经常动态分配的东西都会使您的程序变慢。尝试在解析之前预分配所有缓冲区。计算所有令牌以便为它们预分配内存应该不难。
      3. 使用分析器确定导致减速的原因。
      4. 尽量避免使用iostream的>操作符,自己解析文件。

      一般来说,高效的 C/C++ 解析器实现应该能够在 3 秒内解析 20 兆字节的大文本文件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-08-03
        • 2012-08-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多