使用C/C++读取文件格式以适当的方式

发布于 2024-11-01 23:08:24 字数 564 浏览 1 评论 0原文

您将如何使用 C 或 C++ 读取简单的文件格式?

例如,采用 Wavefront .obj 文件格式,示例:

# 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

由于文件可能非常大,因此我使用运算符 [] 创建了一个中间类 (FileBuffer)。它在内存中只有 4096 字节的文件,并在需要时读取新的部分。 文件格式非常简单,但我不喜欢使用像 flex/bison 这样的东西。这只会让事情变得复杂。

解释这种(类型)文件的正确方法是什么?目前我有很多嵌套的 for/while 循环和许多跟踪计数器。还有许多 switch/elseif 语句。我如何使这段代码可维护并且整体上更加结构化?

谢谢。

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 overall?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

以往的大感动 2024-11-08 23:08:24

如果是我,我会尽可能多地利用标准库:

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 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));
        }
    }
}
醉生梦死 2024-11-08 23:08:24

如果我正确理解格式,每一行都定义
特定类型的数据,其类型由第一个确定
单词。我首先定义一个抽象基类,然后
每种类型线的类的具体实例; ID
在 std::map

为了读取文件,我可能会安装一个过滤streambuf
删除上游的注释和空行。然后
一个简单的循环就可以解决问题:

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 );
    }
}

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 );
    }
}
深海夜未眠 2024-11-08 23:08:24

我将从定义(或获取)文件语法/结构开始。

然后根据该语法为输入流构建一个解析器。

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文