开发者

Read file format using C/C++ in a proper manner

开发者 https://www.devze.com 2023-02-25 07:28 出处:网络
How would you go about reading a simple file format using C or C++? For example, take the Wavefront .obj file format, sample:

How would you go about reading a simple file format using C or C++?

For example, take the Wavefront .obj file format, sample:

# this is a box

o 1

v -0.5 -0.5 0.5
v -0.5 -0.5 -0.5
v -0.5 0.5 -0.5
v -0.5 0.5 0.5
v 0.5 -0.5 0.5
v 0.5 -0.5 -0.5
v 0.5 0.5 -0.5
v 0.5 0.5 0.5

usemtl Default
f 4 3 2 1
f 2 6 5 1
f 3 7 6 2
f 8 7 3 4
f 5 8 4 1
f 6 7 8 5

Since the files can be quite large, I made an intermediate class (FileBuffer) with an operator[]. It only every has 4096 bytes of the file in memory, and reads new parts whenever needed. The file format is really simple, but I don't prefer to use something like flex/bison for this. It would only complicate matters.

What would be a proper way to go about interpreting this (kind of) file? Currently I have a whole lot of nested for/while loops and many counters keeping track. Also many switch/elseif statements. How would I make this code maintainable and more structured ov开发者_如何学Goerall?

Thanks.


If it were me, I'd leverage as much of the standard library as I could:

struct Command { /* Abstract Base Class */ ... };
struct VCommand : Command { std::vector<double> dims; ... }
struct FCommand : Command { std::vector<int> vertexes; ... }
struct FootCommand : Command { enum {LEFT, RIGHT, IN, OUT} e; ... }
std::vector<Command*> commandList; // DANGER: raw pointers
void ParseInput(std::istream& in) {
    // untested code:
    std::string line;
    while(getline(in, line)) {
        std::stringstream lineStream(line);
        std::string command;
        lineStream >> command;
        if(command == "v") {
            std::istream_iterator<double>(lineStream) begin;
            std::istream_iterator<double> end;

            // Add the "v" command to the parse tree
            commandList.push_back(new VCommand(begin, end));
        } else if (command == "f") {
            std::istream_iterator<int>(lineStream) begin;
            std::istream_iterator<int> end;

            // Add the "v" command to the parse tree
            commandList.push_back(new FCommand(begin, end));
        } else if (command == "quit") {
            ...
        } else if (command == "put_your_left_foot_in") {
            ...
            commandList.push_back(new FootCommand(LEFT, IN));
        }
    }
}


If I understand the format correctly, each line defines a specific type of data, with the type determined by the first word. I'd start by defining an abstract base class and a concrete instance of the class for each type of line; I'd register these instances in a std::map<std::string, LineParser*>.

For reading the file, I'd probably install a filtering streambuf to get rid of the comments and the empty lines upstream. Then a simple loop would do the trick:

std::string line;
while ( std::getline( filteredInput, line ) ) {
    std::string keyword;
    std::istringstream toParse( line );
    toParse >> keyword;
    std::map<std::string, LineParser*>::const_iterator
        parser = registry.find( keyword );
    if ( parser == registry.end() ) {
        //  Syntax error: unknown keyword...
    } else {
        parser->parse( toParse );
    }
}


I would start with defining (or obtaining) the file grammar/structure.

Then build a parser for the input stream based on that grammar.

0

精彩评论

暂无评论...
验证码 换一张
取 消