简体   繁体   English

C ++:如何“存储”节点

[英]C++: How do I “store” nodes

So I am trying to read data in the following format from a text file for a simple maze game: 因此,我尝试从一个简单的迷宫游戏的文本文件中读取以下格式的数据:

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

The first column is the current node. 第一列是当前节点。 The second column, the node to its immediate north. 第二列,即紧邻其北部的节点。 Third, to its immediate east, Fourth, to the immediate south, and fifth, to the immediate west. 第三,在它的东面,第四,在它的南面,第五,在它的西面。 Any * represents no node, and the pointer should be set to null. 任何*表示没有节点,并且指针应设置为null。

Keeping in mind that I am a rookie at OOP, I am curious how to go about "saving" this information into a class called Node. 请记住,我是OOP的新手,我很好奇如何将这些信息“保存”到称为Node的类中。

I've got the following class definition: 我有以下类定义:

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

With the code build >> node.name >> node.north >> node.east >> node.south >> node.west; 使用代码build >> node.name >> node.north >> node.east >> node.south >> node.west; the correct characters are read in. 读取正确的字符。

This correctly gets the characters that I want. 这样可以正确获取我想要的字符。 However, once I get to the end of the line, how do I store this node and then move on to the next line and store THAT node? 但是,一旦到达该行的末尾,如何存储该节点,然后继续到下一行并存储该节点? Right now, I've got a do/while loop that loops through this until it reaches the end of file but of course it overwrites everything each time it goes through so the only code it is effectively reading in is the last line of L * * * K. How do I get the first line to save as node A, the second as node B, etc? 现在,我有一个do / while循环,可以循环执行直到它到达文件末尾,但是当然,每次遍历它都会覆盖所有内容,因此有效读取的唯一代码是L *的最后一行* * K.如何使第一行另存为节点A,第二行另存为节点B,依此类推? How do I reference this elsewhere in the program? 我如何在程序的其他地方引用它? Right now, I'm just creating an object node of type Node. 现在,我只是创建Node类型的对象节点。 When I get to the end of the line, I need everything in node to be copied to a new Node, called A. 当我到达该行的末尾时,我需要将node中的所有内容都复制到一个名为A的新Node中。

EDIT: Sorry about the formatting; 编辑:对不起格式; Stack Overflow is not recognizing my 4 spaces as indention of code. 堆栈溢出无法将我的4个空格识别为代码缩进。

I'm not sure if I'm getting what is what you want to get, but if you want to save each element of type Node with a different identifier you may try the next: 我不确定是否要获得什么,但是如果要使用不同的标识符保存Node类型的每个元素,则可以尝试下一个:

pair<char, Node> node_with_name;

And then you can assign the name to the first element doing build >> node_with_name.first; 然后,您可以将名称分配给第一个执行build >> node_with_name.first;元素build >> node_with_name.first; , then put the rest of the elements in the node and assign it in the same way to the second position of the pair . 后,再放入所述节点中的元素的其余部分,以相同的方式分配给它的第二位置pair

And the vector should also be changed in order to use this solution: 为了使用此解决方案,还应更改向量:

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

And finally you would do: 最后,您会这样做:

nodes.push_back(node_with_name);

in every iteration of the loop. 在循环的每次迭代中。


Edit: I think that a map would possibly fit better to your needs. 编辑:我认为一张map可能会更适合您的需求。 I will show you an example: 我会给你看一个例子:

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

And then you can output each node's data with: 然后,您可以使用以下命令输出每个节点的数据:

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

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

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

在文件上使用getline()函数,逐行读取文件,然后将节点信息解析为节点结构。

One thing that's rough for you is that you can't create nodes directly, because early on you'll need to get pointers to nodes you haven't created yet. 对您来说很粗糙的一件事是,您不能直接创建节点,因为在早期,您将需要获得指向尚未创建的节点的指针。 One way to render this problem moot is what you seem to be doing: storing the node letters instead of pointers: 解决此问题的一种方法是您似乎正在做的事情:存储节点字母而不是指针:

#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'; 
}

Then the Maze class holds them all: 然后, 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;
}

Now reading the maze is simply: 现在阅读迷宫很简单:

Maze maze;
build >> maze;

And printing it is something like: 打印它就像:

cout << maze;

There are lots of improvements that can be made here, such as storing pointers directly in the nodes (this requires a two-pass reader: you still have to store the letters, then link all the nodes after reading the whole maze) and removing the names on the nodes and the node map and putting all the code into the Node class (allows you to manage the whole maze from any node; node names are discarded after reading the maze and regenerated when printing it out). 这里可以进行很多改进,例如将指针直接存储在节点中(这需要两遍读取器:您仍然必须存储字母,然后在读取整个迷宫之后链接所有节点)并删除节点和节点映射上的名称,并将所有代码放入Node类(允许您从任何节点管理整个迷宫;在读取迷宫后节点名称将被丢弃,并在打印出来时重新生成)。

Also, I'm assuming you aren't using C++11. 另外,我假设您没有使用C ++ 11。 If you are, then you can simplify the code even further. 如果是这样,则可以进一步简化代码。 For example, this: 例如,这:

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

becomes: 变成:

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

That all said, there are far more robust parsing options than istreams. 综上所述,解析选项比istream强大得多。 Check out Boost::Spirit if you think writing Backus-Naur Form (BNF) is no big deal and want to see the crazy things you can do with operator overloading in C++. 如果您认为编写Backus-Naur Form(BNF)没什么大不了,并且想了解在C ++中使用运算符重载可以做的疯狂事情,请查看Boost :: Spirit。

#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

As I understand the question, you'd want to be able to read in a description of nodes from a file and store them in a data structure in memory. 据我了解的问题,您希望能够从文件中读取节点的描述,并将其存储在内存中的数据结构中。 Looks like a text adventure map. 看起来像是文字冒险地图。

I've copied your maze game map into a text file called "maze.dat" 我已经将您的迷宫游戏地图复制到一个名为“ maze.dat”的文本文件中

Here's a simple program that parses the maze.dat file, line by line, storing each line into a user defined data structure called Node. 这是一个简单的程序,它逐行解析maze.dat文件,并将每一行存储到用户定义的称为Node的数据结构中。 Each node is then placed in another data structure called a vector. 然后将每个节点放在另一个称为向量的数据结构中。

At the end of the program I've printed out each node in the vector so that you can see it matches the original input file. 在程序的最后,我已打印出向量中的每个节点,以便您可以看到它与原始输入文件匹配。 Here is my example: 这是我的示例:

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

This is a pretty simple method to translate the file into a data structure. 这是将文件转换成数据结构的非常简单的方法。 This is useful for loading files, you could then create a way to save maps, hence build a map creator program. 这对于加载文件很有用,然后您可以创建一种保存地图的方法,从而构建一个地图创建程序。

This probably does not help much when it comes to actually using the maze map in the actual game. 在实际游戏中实际使用迷宫贴图时,这可能无济于事。 It's more likely that you will want to retrieve the relevant room depending on whether you want to travel north, east, south, west. 您更有可能要根据是否要向北,向东,向南,向西旅行来检索相关房间。 For this you're going to need to build a graph data structure using std::map as previously described by Str1101 为此,您将需要使用std :: map构建图形数据结构,如先前Str1101所述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM