2013-07-25 45 views
0

所以我想从一个文本文件中一个简单的迷宫游戏读取的格式如下数据:C++:我如何“存储”节点

 
A * B * * 
B * C * A 
C G D * B 
D * * * C 
E I F * * 
F J G * E 
G * H C F 
H * * * G 
I * * E * 
J * K F * 
K * L * J 
L * * * K 

第一列是当前节点。第二列,它的直接向北的节点。第三,它的直接的东部,第四,直接的南部和第五,直接到西部。任何*表示没有节点,并且指针应该设置为空。

请记住,我是OOP的新手,我很好奇如何将这些信息“保存”到一个名为Node的类中。

我有下面的类定义:

public: 
    int BuildGraph(); 
    char name; 
    char north; 
    char east; 
    char south; 
    char west; 
}; 
    std::vector<Node> nodes; 

随着代码build >> node.name >> node.north >> node.east >> node.south >> node.west;正确的字符被读入

这正确地得到我想要的字符。但是,一旦我走到线的尽头,如何存储此节点,然后移动到下一行并存储THAT节点?现在,我已经有了一个do/while循环,循环遍历它直到它到达文件末尾,但是它当然每次都会覆盖所有内容,所以它有效读取的唯一代码是L *的最后一行, * * K.如何将第一行保存为节点A,第二行保存为节点B等?我如何在程序中的其他地方引用它?现在,我只是创建一个Node类型的对象节点。当我到达行的末尾时,我需要将节点中的所有内容复制到一个新的节点,名为A.

编辑:对格式化抱歉;堆栈溢出无法识别我的4个空格作为代码的缩进。

+1

您是否在问如何将节点链接在一起? –

+0

我不确定我在问什么。我想是这样。 我正在阅读文本文件中的所有信息,但一旦它到达行的末尾,我希望保留当前保存的信息,然后为下一行创建一个类型为Node的新对象。 – sakau2007

+0

也许你会发现有用的答案给予下一个问题:http://stackoverflow.com/questions/2323929/istringstream-how-to-do-this – Str1101

回答

0

在文件中使用getline()函数,逐行读取文件并将节点信息解析到节点结构中。

+0

但我不应该有12个不同的节点?就目前而言,我的计划只有一个。除非我每次在循环中创建一个新的。如果可能的话,我想要命名该节点,无论该行中的第一个字符是什么。 – sakau2007

1

我不知道如果我得到什么是你想要得到什么,但如果你想Node类型的每个元素保存不同的标识符,你可以尝试下:

pair<char, Node> node_with_name; 

然后,您可以将名称分配给第一个元素,并执行build >> node_with_name.first; ,然后将其余元素放入节点中,并将其以与第二个元素相同的方式分配给pair的第二个元素。在循环的每次迭代

nodes.push_back(node_with_name); 

和矢量也应以使用该解决方案来改变:

std::vector<pair<char, Node> > nodes; 

最后,你会怎么做。


编辑:我认为一个map可能会更适合您的需求。我会告诉你一个例子:

std::map<char,Node> mymap; 

do { 
    char name;       // to store temporarily each node's name 
    build >> name;    
    build >> mymap[name].north;  // we start getting the data for the node with this name 
    build >> mymap[name].east; 
    build >> mymap[name].south; 
    build >> mymap[name].west; 
}while(! build.eof()); 

然后你就可以输出每个节点的数据:

std::cout << "mymap['A'].north is " << mymap['A'].north << '\n'; 

地图参考:http://www.cplusplus.com/reference/map/map/operator%5B%5D/

对参考:http://www.cplusplus.com/reference/utility/pair/

+0

我觉得这很接近。但是,我仍然不确定究竟发生了什么。 我得到了程序来编译你的代码片段,但什么是node_with_name?当我尝试cout A.name或A.east时,编译器告诉我没有声明。我在这里错过了什么?

 \t pair node_with_name; \t do \t { \t build >> node_with_name.first; \t \t build >> node.name >> node.north >> node.east >> node.south >> node.west; \t \t nodes.push_back(node_with_name); \t \t } while(! build.eof()); 
sakau2007

+0

我之前编辑过答案,告诉我它是否适合你的需求。 – Str1101

0

有一件事对你来说粗糙的是,你不能直接创建节点,因为早期你需要获得指向尚未创建的节点的指针。渲染这个问题没有实际意义的一种方法是你仿佛在做:存储节点字母代替指针:

#include <iostream> 
class Maze; // Holds all the nodes; defined later. 

class Node 
{public: 
    static const char kNoLink = '*'; 
    Node(): north(kNoLink), east(kNoLink), south(kNoLink), west(kNoLink) {} 
private: 
    char name; 
    char north; 
    char east; 
    char south; 
    char west; 
    friend std::istream& operator>>(std::istream&, Node&); 
    friend std::ostream& operator<<(std::ostream&, const Node&); 
    friend class Maze; 
}; 

std::istream& operator>>(std::istream& is, Node& node) 
{ 
    return is >> node.name >> node.north >> node.east >> node.south >> node.west; 
} 

std::ostream& operator<<(std::ostream& os, const Node& node) 
{ 
    return os << node.name << ' ' << node.north << ' ' << node.east << ' ' 
     << node.south << ' ' << node.west << '\n'; 
} 

然后Maze类包含所有这些:

#include <map> 

class Maze 
{public: 
    Maze() {} 
    ~Maze(); 
    Node* GetNode(char name) 
    { 
     NodeMap::iterator ni = nodeMap.find(name); 
     return ni == nodeMap.end()? 0: *ni; 
    } 
    Node* GoNorth(Node* start) { return GetNode(start->north); } 
    Node* GoEast(Node* start) { return GetNode(start->east); } 
    Node* GoSouth(Node* start) { return GetNode(start->south); } 
    Node* GoWest(Node* start) { return GetNode(start->west); } 
private: 
    typedef std::map<char, Node*> NodeMap; 
    NodeMap nodeMap; 
    friend std::istream& operator>>(std::istream&, Maze&); 
    friend std::ostream& operator<<(std::ostream&, const Maze&); 
}; 

Maze::~Maze() 
{ 
    // While the map itself will get properly destroyed, its contents won't. 
    // So we have to delete the nodes in the map ourselves. 
    // For std::map, i->first is the key and i->second is the value. 
    for(NodeMap::iterator i = nodeMap.begin(); i != nodeMap.end(); ++i) 
     delete i->second; 
} 

std::istream& operator>>(std::istream& is, Maze& maze) 
{ 
    while(is) 
    { 
     Node* newNode = new Node; 
     is >> newNode; 
     maze.nodeMap[newNode.name] = newNode; 
    } 
    return is; 
} 

std::ostream& operator<<(std::ostream& os, const Maze& maze) 
{ 
    for(NodeMap::const_iterator i = nodeMap.begin(); i != nodeMap.end(); ++i) 
     os << *(i->second); 
    return os; 
} 

现在阅读的迷宫简单地说:

Maze maze; 
build >> maze; 

和印刷是一样的东西:

cout << maze; 

在这里可以做出很多改进,比如直接在节点中存储指针(这需要一个双通读取器:您仍然必须存储字母,然后在读完整个迷宫后链接所有节点),并删除节点和节点映射上的名称,并将所有代码放入Node类中(允许您从任何节点管理整个迷宫;节点名称在读完迷宫后被丢弃,并在打印出来时被重新生成)。

另外,我假设你没有使用C++ 11。如果你是,那么你可以进一步简化代码。例如,这样的:

for(NodeMap::const_iterator i = nodeMap.begin(); i != nodeMap.end(); ++i) 
     os << *(i->second); 

变为:

for(auto i: nodeMap) 
     os << *(i->second); 

这一切都表示,有远比istreams更强大的解析选项。如果您认为编写Backus-Naur表单(BNF)没有什么大不了,并希望看到用C++中的运算符重载可以做的疯狂事情,请查阅Boost :: Spirit。

0
#include <iostream> 
#include <iterator> 
#include <vector> 
#include <algorithm> 

struct node 
{ 
    char name; 
    char north; 
    char east; 
    char south; 
    char west; 
}; 

std::istream& operator >> (std::istream& is, node& n) 
{ 
    is >> n.name >> n.north >> n.east >> n.south >> n.west; 
    return is; 
} 

std::ostream& operator << (std::ostream& os, const node& n) 
{ 
    os << n.name << ' ' << n.north << ' ' << n.east << ' ' << n.south << ' ' << n.west; 
    return os; 
} 

int main(int argc, char* argv[]) 
{ 
    std::vector<node> nodes; 

    node n; 

    while(std::cin >> n) 
     nodes.push_back(n); 

    std::copy(nodes.begin(), nodes.end(), std::ostream_iterator<node>(std::cout,"\n")); 
    return 0; 
} 

$ cat nodes.txt | ./a.out 
A * B * * 
B * C * A 
C G D * B 
D * * * C 
E I F * * 
F J G * E 
G * H C F 
H * * * G 
I * * E * 
J * K F * 
K * L * J 
L * * * K 
0

正如我所理解的问题,你希望能够从文件中读取节点的描述并将它们存储在内存中的数据结构中。看起来像一个文字冒险地图。

我复制你的迷宫游戏地图到名为文本文件“maze.dat”

这里有一个简单的程序,解析maze.dat文件,一行行,存储每个行成一个用户定义的数据称为节点的结构。然后将每个节点放入另一个称为矢量的数据结构中。

在程序结束时,我已经打印出矢量中的每个节点,以便您可以看到它与原始输入文件相匹配。这是我的例子:

#include <iostream> 
#include <fstream> 
#include <vector> 

// storing each node in a data structure 
class Node 
{ 
public: 
    Node(char name, char north, char east, char south, char west) 
    { 
     this->name = name; 
     this->north = north; 
     this->east = east; 
     this->south = south; 
     this->west = west; 
    }; 

    char name; 
    char north; 
    char east; 
    char south; 
    char west; 
}; 

// function to print out a node 
void print_node(Node n) 
{ 
    std::cout << n.name << " " << n.north << " " << n.east << " " << n.south << " " << n.west << " " << std::endl; 
} 

int main(int argc, const char * argv[]) 
{ 
    // first off let's read in our maze data file 
    std::ifstream maze_file("maze.dat"); 

    // create somewhere to store our nodes 
    std::vector<Node> nodes; 

    // check that we opened the file, then parse each line 
    if(maze_file.is_open()) 
    { 
     while(maze_file.good()) 
     { 
      // temporary node_data for each line in the file 
      std::string node_data; 

      // read the current line 
      getline(maze_file, node_data); 

      // parse the line into tokens (e.g. A, ,*, ,B, ,*, ,*) 
      std::vector<char> tokens(node_data.begin(), node_data.end()); 

      // strip out the blanks ' ' (e.g. A,*,B,*,*) 
      tokens.erase(std::remove(tokens.begin(), tokens.end(), ' '), tokens.end()); 

      // there should be 5 tokens for a node description 
      if(tokens.size() == 5) 
      { 
       Node node(tokens[0], tokens[1], tokens[2], tokens[3], tokens[4]); 
       nodes.push_back(node); 
      } 
      else 
       std::cout << "There weren't 5 tokens in the node description, there were: " << tokens.size() << std::endl; 
     } 

     // clean-up the open file handle 
     maze_file.close(); 
    } 
    else 
     std::cout << "Unable to open file maze.dat"; 

    // now we can prove that we've stored the nodes in the same way as they were in the file 
    // let's print them out from the vector of nodes 
    std::for_each(nodes.begin(), nodes.end(), print_node); 

    return 0; 
} 

这是一个非常简单的方法来将文件转换成数据结构。这对于加载文件非常有用,您可以创建一种保存地图的方式,从而构建地图创建者程序。

这对于在实际游戏中实际使用迷宫地图可能没有多大帮助。根据你是否想要往北,东,南,西旅行,你很可能想要找回相关的房间。为此,您将需要使用std :: map构建一个图形数据结构,如前所述Str1101