2011-04-15 73 views
0

如何使用C或C++阅读简单的文件格式?以适当的方式使用C/C++读取文件格式

例如,采取波前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语句。我怎样才能使这个代码更易于维护和更加结构化?

谢谢。

+1

你问关于C或C++?它们是不同的语言,所提供的解决方案也可能不同。 – 2011-04-15 16:02:14

+3

@Rob Adams OP“用操作符[]创建了一个中间类,”听起来像C++ – Cubbi 2011-04-15 16:06:25

回答

2

如果是我,我会充分利用尽可能多的标准库的,因为我可以:

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

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

然后基于该语法构建输入流的解析器。

1

如果我理解正确的格式,每一行定义一个 特定类型的数据,与该类型由第一个 字来确定。我首先定义一个抽象基类,并为每种类型的行定义一个类的具体实例;我会 在std::map<std::string, LineParser*>中注册这些实例。

为了读取文件,我可能会安装过滤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); 
    } 
}