2

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

需要注意的几点:

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

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

以下是我的问题:

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

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

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

谢谢您的帮助!

4

3 回答 3

2

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

于 2010-06-24T15:08:55.443 回答
0

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

  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]);
}
于 2010-06-24T16:15:28.250 回答
0

这篇文章应该会有所帮助。

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

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

于 2010-06-24T16:41:57.840 回答