【问题标题】:Read file format using C/C++ in a proper manner以正确的方式使用 C/C++ 读取文件格式
【发布时间】:2011-04-16 02:45:28
【问题描述】:

您将如何使用 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

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

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

谢谢。

【问题讨论】:

  • 您问的是 C 还是 C++?它们是不同的语言,提供的解决方案也可能不同。
  • @Rob Adams OP“用 operator[] 制作了一个中间类”,听起来像 C++
  • stackoverflow.com/questions/2908854/… 可能会提供一个合理的起点。
  • 删除了与不一致的[c]标签“我用operator[]创建了一个中间类(FileBuffer)。”
  • 我实际上也对 C 方法感兴趣,但首选 C++ ;)。亚当斯的方式看起来像一个我从未想过的简单方法:o

标签: c++ file format


【解决方案1】:

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

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

【讨论】:

  • 如果支持stringstreamsstream 将被弃用
  • @Potatoswatter - 谢谢!似乎每次我提到一个(或另一个)时,我都会混淆这两个。我编辑了帖子。
  • “顶点”的复数形式是“顶点”,而不是“顶点”。
【解决方案2】:

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

为了读取文件,我可能会安装一个过滤流缓冲区 摆脱 cmets 和上游的空行。然后 一个简单的循环就可以解决问题:

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-19
      • 1970-01-01
      • 2012-05-03
      • 2010-12-02
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多