从字符串流中读取格式化的行

时间:2014-05-02 22:19:36

标签: c++

为了从文本文件加载大量顶点,我将整个文件加载到内存中,然后我想扫描三行浮动的每一行。 以下是有效的,但我想知道它是好还是浪费。

std::stringstream sstr;
sstr << file.rdbuf();    // file is an ifstream

for (uint i=0; i<num_verts; ++i)
{
    sstr.getline(line, bufsz);
    std::istringstream iss(line);
    iss >> verts[i].x >> verts[i].y >> verts[i].z;
}

在参考网站cplusplus.com上,我找到了istringstream的以下内容:

  

(2)初始化构造函数:构造一个带有str副本作为内容的istringstream对象。

因此,如果istringstream在构建时真正复制每个字符串,那么这非常浪费,特别是因为我已经有了一个字符串流。

sscanf相比,上述情况确实相当缓慢:1.94秒与sscanf的0.56秒相比。

是否在初始化iss时复制了字符串?

如何同时读取格式化的值,同时仅使用字符串流在字符串中前进?

或者不那么具体:上述案例的C ++方法与sscanf同样表现如何?

1 个答案:

答案 0 :(得分:2)

好的,按照我的评论。我为你做了一些测试。

<强>代码:

bloated_read.cpp:

#include <fstream>
#include <sstream>
#include <vector>

struct Vert {
 double x,y,z;
};

int main(){
const int num_verts = 1000000;
const int buff_size = 1024;
std::vector<Vert> verts(num_verts);
std::ifstream file("numbers");
std::stringstream sstr;
sstr << file.rdbuf();    // file is an ifstream
char *line = new char [buff_size];

for (int i=0; i<num_verts; ++i)
{
    sstr.getline(line, buff_size);
    std::istringstream iss(line);
    iss >> verts[i].x >> verts[i].y >> verts[i].z;
}

return 0;
}

请注意,我跳过了以下代码的常见包含和Vert定义。

slimmed_read.cpp:

int main(){
const int num_verts = 1000000;
std::vector<Vert> verts(num_verts);
std::ifstream file("numbers");

for (int i=0; i<num_verts; ++i)
{
    file >> verts[i].x >> verts[i].y >> verts[i].z;
}

return 0;
}

sscanf_read.cpp:

#include <cstdio> //sscanf
int main(){
const int num_verts = 1000000;
const int buff_size = 1024;
std::vector<Vert> verts(num_verts);
std::ifstream file("numbers");
char *line = new char [buff_size];

for (int i=0; i<num_verts; ++i)
{
    file.getline(line, buff_size);
    sscanf(line, "%f %f %f", &verts[i].x, &verts[i].y, &verts[i].z );
}

return 0;
}

<强>结果:

我为num_verts 10 ^ 5和10 ^ 6做了两次测试,所以我改变了输入文件内容和代码中的相应行。总挂钟运行时间:

size | bloated | slim   | sscanf
10^5 |  0.401s | 0.242s | 0.190s
10^6 |  4.041s | 2.392s | 1.896s

似乎一致。如果您使用ifstream来解析sscanf,则可以直接使用line刷掉40%,并获得25%以上的费用。 10^6的文件有56 444KB,因此解析效率为29,07MB/s。我会说还有一些改进的余地,但是在我的系统中,我认为这是在硬盘绑定的边缘。

<强>结论:

如果不需要进一步加速,我会选择sscanf版本。实现和理解它仍然相当简单。还要检查硬盘的速度,确保它值得一试。

也不应该认为c和c ++样式之间没有混合访问数据。使用c ++工具将数据提取到缓冲区中,并使用c函数读取缓冲区。我想没关系。