4

(如果我问错这个问题,我很抱歉,这是我第一次在论坛上写)

当我开始在我的 SFML - Game 上编程时,我有一本非常古老的书,它非常类似于 C(例如 atoi() 的推荐;)。
现在我有了一本新的 C++(包括 C++11)书,我想用更新的代码重写旧行。

我将 Tiles 保存在一个像这样存储的文件中:

[0-0,15-1|22,44] [0-1|0]
[4-0,10-1,3-1|0] [0-5,5-5|0]

这意味着:
[...] 描述 Tile
0-0 等是纹理表上的 xy 位置
22 等是将被触发的事件。
事件的数量和 sf::Vector2i 不应该经常设置。

Tiles 是从另一个类中单独取出的,该类管理整个 Tilemap。

现在我的问题:我不知道我应该如何将字符串中的数字推送到两个向量中?我的代码:

class Tile{
     private:
          std::deque<sf::Sprite> tile;
      std::deque<int> event;
     public:

    Tile(sf::Texture& texture, std::deque<sf::Vector2i>&& ctor_texturerects, std::deque<int>&& ctor_events);//This one is working fine

    Tile(sf::Texture& texture, std::stringstream&& ctor_stream/*reads the Tile*/){

      std::deque<sf::Vector2i> temp_texturerects;
      std::deque<int>temp_events;
      /*TODO: filter the stringstream and push them into the containers*/
      Tile::Tile(texture,std::move(temp_texturerect),std::move(temp_events));
}

如果你能给我另一个解决方案,我也会很高兴,比如将 sf::Vector2i 更改为更好的解决方案或给我一个更好的流和类概念
在此先感谢
Xeno Ceph

编辑:
我做了一些解决方法:(
我将输入流更改为普通字符串)
但是代码看起来不太好必须有一个更简单的解决方案

Tile::  Tile(sf::Texture& texture, std::string&& ctor_string){
    std::deque<sf::Vector2i> temp_texturerects;
    std::deque<int> temp_events;
    std::stringstream strstr;


    for(int i=0; i<ctor_string.size(); ++i){
        while(ctor_string[i]!='|'){

            while(ctor_string[i] != ','){
                strstr << ctor_string[i];
            }
            sf::Vector2i v2i;
            strstr >> v2i.x >> v2i.y;
            temp_texturerects.push_front(v2i);
            strstr.str("");
        }
        while(ctor_string[i]!=']'){
            while(ctor_string[i] != ','){
                strstr << ctor_string[i];
            }
            int integer;
            strstr  >> integer;
            temp_events.push_front(integer);
            strstr.str("");
        }
    }
    Tile::Tile(texture, std::move(temp_texturerects), std::move(temp_events));
} 

有人有更好的解决方案吗?

4

2 回答 2

0

如果我正确理解你的问题,你有一些形式的字符串

[0-0,15-1|22,44] [0-1|0]
[4-0,10-1,3-1|0] [0-5,5-5|0]

并且您想要提取 2 种类型的数据 - 位置(例如 0-0)和事件(例如 22)。您的问题是如何干净地提取这些数据,丢弃[and]字符等。

解决此问题的一种好方法是使用对字符串流getline进行操作的函数,该函数继承自 std::istream ( http://www.cplusplus.com/reference/string/string/getline/ )。它可以采用自定义分隔符,而不仅仅是换行符。因此,您可以使用'[','|'']'作为不同的分隔符并按逻辑顺序解析它们。

例如,由于您的字符串只是瓷砖的集合,您可以将其拆分为多个函数 -和ParseTile,如下所示:ParsePositionsParseEvents

void Tile::ParseInput(stringstream&& ctor_string) {

  //extract input, tile by tile
  while(!ctor_string.eof()) {
    string tile;

    //you can treat each tile as though it is on a separate line by
    //specifying the ']' character as the delimiter for the "line"
    getline(ctor_string, tile, ']');
    tile += "]"; //add back the ']' character that was discarded from the getline

    //the string "tile" should now contain a single tile [...], which we can further process using ParseTile
    ParseTile(tile);
  }

}

ParseTile 函数:

void Tile::ParseTile(string&& tile) {
  //input string tile is e.g. " [0-0, 15-1|22,44]"

  string discard; //string to hold parts of tile string that should be thrown away
  string positions; //string to hold list of positions, separated by ','
  string events; //string to hold events, separated by ','

  //create stringstream from input
  stringstream tilestream(tile);
  //tilestream is e.g. "[0-0,15-1|22,44]"

  getline(tilestream, discard, '['); //gets everything until '['
  //now, discard is " [" and tilestream is "0-0,15-1|22,44]"

  getline(tilestream, positions, '|');
  //now, positions is "0-0,15-1" and tilestream is "22,44]"

  getline(tilestream, events,']');
  //now, events is "22,44" and tilestream is empty

  ParsePositions(positions);

  ParseEvents(events);


}

您可以编写自己的 ParsePositions 和 ParseEvents 函数,它们基本上将是使用“,”作为分隔符的更多 getline 调用(循环直到字符串结束)。

于 2013-03-31T10:08:20.107 回答
0

我建议要么手动编写适当的解析器(与其他答案建议的不同),要么使用适当的解析框架,如 Boost Spirit。

后者的优点是您可以“免费”获得可调试性、可组合性、属性等。这是我能想到的最简单的例子:

struct TileData
{
    std::deque<sf::Vector2i> texturerects;
    std::deque<int>          events;
};

typedef std::deque<TileData> TileDatas;

template <typename It>
struct parser : qi::grammar<It, TileDatas(), qi::space_type>
{
    parser() : parser::base_type(start)
    {
        using namespace qi;

        v2i = (int_ >> '-' >> int_) 
            [ _val = phx::construct<sf::Vector2i>(_1, _2) ];

        tiledata = 
            (v2i  % ',') >> '|' >>
            (int_ % ',');

        start = *('[' >> tiledata >> ']');
    }
    private:
    qi::rule<It, sf::Vector2i(), qi::space_type> v2i;
    qi::rule<It, TileData(),     qi::space_type> tiledata;
    qi::rule<It, TileDatas(),    qi::space_type> start;
};

添加一些代码来测试它,在http://liveworkspace.org/code/3WM0My$1上查看它,输出:

Parsed: TileData {
    texturerects: deque<N2sf8Vector2iE> {v2i(0, 0), v2i(15, 1), }
    events:       deque<i> {22, 44, }
}
Parsed: TileData {
    texturerects: deque<N2sf8Vector2iE> {v2i(0, 1), }
    events:       deque<i> {0, }
}
Parsed: TileData {
    texturerects: deque<N2sf8Vector2iE> {v2i(4, 0), v2i(10, 1), v2i(3, 1), }
    events:       deque<i> {0, }
}
Parsed: TileData {
    texturerects: deque<N2sf8Vector2iE> {v2i(0, 5), v2i(5, 5), }
    events:       deque<i> {0, }
}

完整代码:

#define BOOST_SPIRIT_USE_PHOENIX_V3
// #define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;
namespace phx= boost::phoenix;

// liveworkspace.org doesn't have SFML
namespace sf { struct Vector2i { int x, y;  Vector2i(int ax=0, int ay=0) : x(ax), y(ay) {} }; }

struct TileData
{
    std::deque<sf::Vector2i> texturerects;
    std::deque<int>          events;
};

BOOST_FUSION_ADAPT_STRUCT(TileData,
        (std::deque<sf::Vector2i>, texturerects)
        (std::deque<int>, events))

typedef std::deque<TileData> TileDatas;

template <typename It>
struct parser : qi::grammar<It, TileDatas(), qi::space_type>
{
    parser() : parser::base_type(start)
    {
        using namespace qi;

        v2i = (int_ >> '-' >> int_) 
            [ _val = phx::construct<sf::Vector2i>(_1, _2) ];

        tiledata = 
            (v2i  % ',') >> '|' >>
            (int_ % ',');

        start = *('[' >> tiledata >> ']');
    }
    private:
    qi::rule<It, sf::Vector2i(), qi::space_type> v2i;
    qi::rule<It, TileData(),     qi::space_type> tiledata;
    qi::rule<It, TileDatas(),    qi::space_type> start;
};

typedef boost::spirit::istream_iterator It;

std::ostream& operator<<(std::ostream& os, sf::Vector2i const &v) { return os << "v2i(" << v.x << ", " << v.y << ")"; }

template <typename T> std::ostream& operator<<(std::ostream& os, std::deque<T> const &d) {
    os << "deque<" << typeid(T).name() << "> {";
    for (auto& t : d) os << t << ", ";
    return os << "}";
}

std::ostream& operator<<(std::ostream& os, TileData const& ttd) {
    return os << "TileData {\n"
        "\ttexturerects: " << ttd.texturerects << "\n"
        "\tevents:       " << ttd.events << "\n}";
}

int main()
{
    parser<It> p;

    std::istringstream iss(
            "[0-0,15-1|22,44] [0-1|0]\n"
            "[4-0,10-1,3-1|0] [0-5,5-5|0]");

    It f(iss), l;

    TileDatas data;
    if (qi::phrase_parse(f,l,p,qi::space,data))
    {
        for (auto& tile : data)
        {
            std::cout << "Parsed: " << tile << "\n";
        }
    }

    if (f != l)
    {
        std::cout << "Remaining unparsed: '" << std::string(f, l) << "'\n";
    }
}
于 2013-03-31T11:56:10.387 回答