如何解析一组国际象棋动作的字符串并单独存储每个动作 C++

How to parse a string of a set of chess moves and store each move individually C++

所以我正在读取一个 .txt 文件,其中包含多组国际象棋走法。我能够从文件中读取数据并将该行插入到字符串中。

单个国际象棋走法示例如下所示:

1. e4 e5

我编写了以下函数来解析单个棋步:

void parseSingleChessMove(string move)
  {
    this->moveNumber = stoi(move.substr(0, move.find("."))); 
    this->move[0] = move.substr(move.find_first_of(" ")+1, move.find_last_of(" ")-move.find_first_of(" ")-1);
    this->move[1] = move.substr(move.find_last_of(" ")+1);
  }

我正在解析字符串并将其存储在自定义的 Move Class 中,因此使用 'this' 运算符。此功能完美运行并存储单个国际象棋移动的每个字段。 move[0] 存储第一个着法,move[1] 存储第二个着法,而 moveNumber 数据成员存储着法的编号。

我正在创建一个 Move Class 数组,以便按顺序存储国际象棋比赛的每一步。然而,一套完整的国际象棋走法可能看起来像这样:

1. Nf3 Nf6 2. c4 c6 3. g3 g6 4. b3 Bg7 5. Bb2 O-O 6. Bg2 d5 7. O-O Bf5 8. d3
Nbd7 9. Nd4 e6 10. h3 h5

我很难弄清楚如何从一组国际象棋动作中将这些单独的动作存储在 Move Class 数组中。

主要问题是只读取字符串直到找到移动编号。然后我需要获得一个移动的子字符串(类似于 4. b3 Bg7 然后使用上述函数解析这个单一的国际象棋移动以便我可以存储 moveNumber=4、move[0]="b3" 和 move[1 ]="Bg7" 最后存储到数组类型 Move Class 的相应索引中。然后重复此操作,直到所有移动都一一存储,我们到达字符串的末尾。

编辑:这是我的 class 定义:

class MoveNode {
  public:
    array<string, 2> move; 
    int moveNumber; 

    void parseSingleChessMove(string move)
    {
      this->moveNumber = stoi(move.substr(0, move.find("."))); 
      this->move[0] = move.substr(move.find_first_of(" ")+1, move.find_last_of(" ")-move.find_first_of(" ")-1);
      this->move[1] = move.substr(move.find_last_of(" ")+1);
    }
}

我将所有动作存储在这个数组中: MoveNode *setofMoves = new MoveNode[totalMoves];

您可以为此使用正则表达式:

  • 重复搜索的模式是:(\d+)\. 一个或多个数字(我们要捕获,因此使用括号),后跟一个点;然后 \s+([^\s]+) 一个或多个空格,然后是一个或多个非空格(我们捕获后者);我们重复这种模式两次,每次移动一次;最后 (:?\s+|$),一个或多个空格 \s+| 表达式的末尾 $ 因为输入行可能以第二步结束(我们不捕获这个组 (:?)).
    我们使用 std::regex 来存储模式,将其全部包装在 R"()" 中,以便我们可以编写原始表达式。
  • while 循环做了一些事情:它搜索与 regex_search 的下一个匹配,提取捕获的组(移动编号,移动 0 和移动 1),并更新输入行,这样下一次搜索将从当前搜索结束的地方开始。
    matches 是一个数组,其第一个元素 matches[0]line 匹配整个模式的部分,下一个元素对应于模式的捕获组。

[Demo]

#include <iostream>  // cout
#include <regex>  // regex_search, smatch

int main() {
    std::string line{"1. Nf3 Nf6 2. c4 c6 3. g3 g6 4. b3 Bg7 5. Bb2 O-O 6. Bg2 d5 7. O-O Bf5 8. d3 Nbd7 9. Nd4 e6 10. h3 h5"};
    std::regex pattern{R"((\d+)\.\s+([^\s]+)\s+([^\s]+)(:?\s+|$))"};
    std::smatch matches{};
    while (std::regex_search(line, matches, pattern))
    {
        std::cout
            << "moveNum=" << matches[1] << ", "
            << "move[0]=" << matches[2] << ", "
            << "move[1]=" << matches[3] << "\n";
        line = matches.suffix();
    }
}

// Outputs:
//   moveNum=1, move[0]=Nf3, move[1]=Nf6
//   moveNum=2, move[0]=c4, move[1]=c6
//   moveNum=3, move[0]=g3, move[1]=g6
//   moveNum=4, move[0]=b3, move[1]=Bg7
//   moveNum=5, move[0]=Bb2, move[1]=O-O
//   moveNum=6, move[0]=Bg2, move[1]=d5
//   moveNum=7, move[0]=O-O, move[1]=Bf5
//   moveNum=8, move[0]=d3, move[1]=Nbd7
//   moveNum=9, move[0]=Nd4, move[1]=e6
//   moveNum=10, move[0]=h3, move[1]=h5

@rturrado 展示了如何使用正则表达式做到这一点,但是我会犹豫这样做,因为 std::regex 很重,需要很多有关正则表达式的知识以有效地使用它。相反,我认为使用 istreamoperator>>.

更容易完成
void parse_moves(std::istream& input)
{
    int move_number;
    char dot;
    std::string move_fisrt, move_second;
    int index = 0;
    while(input >> move_number >> dot >> move_first >> move_second)
    {
        setofMoves[index] = MoveNode{{move_first, move_second}, move_number};
        ++index;
    }
}

此处 while(is >> ...) 将继续解析文本,只要它遵循该模式。