如何解析一组国际象棋动作的字符串,并将每个动作单独存储在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);
}

我正在分析字符串,并将其存储在自定义的移动类中,因此使用了'this'运算符。此功能运行良好,可存储单个国际象棋动作的每个区域。move[0]存储第一个移动,move[1]存储第二个移动,而moveNumber数据成员存储移动的播放编号。

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

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 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];

@rturrado展示了如何使用regex实现这一点,但我不愿意这样做,因为std::regex很重,需要大量有关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 >> ...)将继续解析文本,只要它遵循该模式。

您可以使用正则表达式来实现这一点:

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

【演示】

#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

最新更新