1

Wavefront OBJ ファイル形式、プレーン テキスト ファイルのパーサーを作成したいと考えています。例はここで見ることができます: people.sc.fsu.edu/~jburkardt/data/obj/diamond.obj.

ほとんどの人は、古い scanf を使用してこの形式を 1 行ずつ解析しますが、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

それは本当にあなたがテキストを解析しようとしている方法に依存します。これを行う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を最小化することだと思います。コレクションを2回繰り返すことになるので、このソリューションが本当に役立つかどうかはわかりません。戻って同じファイルを何度も読み続ける必要がある場合は、ファイルをメモリにキャッシュするための速度が確実に向上しますが、ファイルのメモリマッピングや通常のファイルの使用など、これを行う簡単な方法があります。アクセス。本当に心配な場合は、IOから読み取るときにファイルを直接処理するのではなく、このようなソリューションのプロファイリングを試してください。

于 2012-07-14T11:09:18.467 に答える
1

char[]a (または a )に生データがあり、unsigned char[]その長さがわかっている場合、入力のみを書き込むのは非常に簡単です。シークはサポートされていないためstreambuf、 を作成しstd::istream て使用できますstd::getline。ただ電話してください:

setg( start, start, start + length );

コンストラクターで。(他には何も必要ありません。)

于 2012-07-13T14:35:41.043 に答える