2011-04-15 94 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); 
    } 
}