1

我想为 Wavefront OBJ 文件格式,纯文本文件编写一个解析器。示例可以在这里看到:people.sc.fsu.edu/~jburkardt/data/obj/diamond.obj。

大多数人使用旧的 scanf 逐行解析这种格式,但是我更愿意一次加载整个文件以减少 IO 操作计数。有没有办法逐行解析这种缓冲数据?

void ObjModelConditioner::Import(Model& asset)
{
    uint8_t* buffer = SyncReadFile(asset.source_file_info());

    delete [] buffer;
}

或者将整个文件加载到一个字符串中并尝试解析它会更好吗?

4

4 回答 4

4

过了一会儿,我似乎找到了足够(和简单)的解决方案。由于我的目标是创建资产调节管道,因此代码必须能够有效地处理大量数据。数据可以一次读入string,一旦加载,stringstream就可以用这个字符串初始化。

std::string data;
SyncReadFile(asset.source_file_info(), data);

std::stringstream data_stream(data);
std::string line;

然后我简单地打电话getline()

while(std::getline(data_stream, line)) 
{        
    std::stringstream line_stream(line);
    std::string type_token;

    line_stream >> type_token;
    if (type_token == "v") {
        // Vertex position
        Vector3f position;
        line_stream >> position.x >> position.y >> position.z;
        // ...
    }
    else if (type_token == "vn") {
        // Vertex normal
    }
    else if (type_token == "vt") {
        // Texture coordinates
    }
    else if (type_token == "f") {
        // Face
    }
}
于 2012-07-14T18:35:42.297 回答
2

这是一个将 char 数组拆分为字符串向量的函数(假设每个新字符串都以 '\n' 符号开头):

#include <iostream>
#include <vector>

std::vector< std::string >split(char * arr)
{
    std::string str = arr;
    std::vector< std::string >result;
    int beg=0, end=0;//begining and end of each line in the array
    while( end = str.find( '\n', beg + 1 ) )
    {
        if(end == -1)
        {
            result.push_back(str.substr(beg));
            break;
        }
        result.push_back(str.substr(beg, end - beg));
        beg = end;
    }
    return result;
}

这是用法:

int main()
{
    char * a = "asdasdasdasdasd \n asdasdasd \n asdasd";
    std::vector< std::string >result = split(a);
}
于 2012-07-13T14:20:22.487 回答
1

如果您在 a char[](或 a unsigned char[])中获得了原始数据,并且您知道它的长度,那么只编写一个输入非常简单,不支持streambuf允许您创建 anstd::istreamstd::getline在其上使用的 seek。只需致电:

setg( start, start, start + length );

在构造函数中。(不需要其他任何东西。)

于 2012-07-13T14:35:41.043 回答
1

这实际上取决于您将如何解析文本。一种方法是简单地将数据读入字符串向量。我假设您已经涵盖了诸如可扩展性/内存使用等问题。

std::vector<std::string> lines;
std::string line;
ifstream file(filename.c_str(), ios_base::in);
while ( getline( file, line ) )
{
    lines.push_back( line );
}
file.close();

这会将您的文件缓存在lines. 接下来你需要通过线路

for ( std::vector<std::string>::const_iterator it = lines.begin();
      it != lines.end(); ++it)
{
    const std::string& line = *it;
    if ( line.empty() )
         continue;

    switch ( line[0] )
    {
        case 'g':
            // Some stuff
            break;
        case 'v':
            // Some stuff
            break;
        case 'f':
            // Some stuff
            break;
        default:
            // Default stuff including '#' (probably nothing)
    }
}

当然,这非常简单,很大程度上取决于您要对文件做什么。

您作为示例给出的文件大小几乎不可能导致 IO 压力(除非您使用一些非常轻量级的设备),但如果您一次读取多个文件,我想这可能是一个问题。

我认为您在这里关心的是最小化 IO,我不确定这个解决方案是否真的有那么大的帮助,因为您将迭代一个集合两次。如果您需要返回并一遍又一遍地读取同一个文件,那么将文件缓存在内存中肯定会加快速度,但也有同样简单的方法可以做到这一点,例如内存映射文件和使用普通文件访问。如果您真的很担心,请尝试分析这样的解决方案,而不是在从 IO 读取时直接处理文件。

于 2012-07-14T11:09:18.467 回答