正则表达式偶尔会给出错误的答案

本文关键字:错误 答案 出错 偶尔 正则表达式 | 更新日期: 2023-09-27 18:17:05

我正在尝试匹配国际象棋符号。我有一个c#正则表达式像这样:

"(?:[PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:'=[PNBRQK])?|O(-?O){1,2})['+#]?('s*['!'?]+)?";

[我不介意c# YACC词法分析器用于短代数表示法(SAN),但我现在使用regex:]

<move> ::= <move number><move descriptor>
<move number> ::= <digit>[<digit>...]{'.' | '...'}
<move descriptor> ::= <from square><to square>[<promoted to>]
<square>        ::= <file letter><rank number>
<file letter>   ::= 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'
<rank number>   ::= '1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'
<promoted to>   ::= 'q'|'r'|'b'|'n'
<Piece symbol> ::=  'P' | 'N' | 'B' | 'R' | 'Q' | 'K'
<SAN move descriptor piece moves>   ::= <Piece symbol>[<from file>|<from rank>|<from 
square>]['x']<to square>
<SAN move descriptor pawn captures> ::= <from file>[<from rank>] 'x' <to square>[<promoted to>]
<SAN move descriptor pawn push>     ::= <to square>[<promoted to>]

有时上面的正则表达式匹配得太多了,例如这些前几步是这样匹配的(我在匹配之前删除了移动数):

1e4d5 
2exd5Nf6 
3d4Qxd5
4c4Qd6
5Nf3c5
6Be3cxd4
7Nxd4a6
8Be2e5
9Nf3Nc6
10O-OQxd1
11Rxd1Be7
12Nc3Be6
13Nd5Bd8
14Nb6Rb8
15Ng5Bf5
16Bf3e4
17Be2h6
18Nh3Bxh3
19gxh3O-O
20Nd7Nxd7
21Rxd7Bf6
22Bf4Rfd8
23Rad1Be5
24Bxe5Rxd7
25Rxd7Nxe5
26Re7Nf3+
27Kg2f5
28Bxf3exf3+
29Kxf3Rc8
30b3b5
31cxb5axb5
32Rb7Ra8
33Rxb5Rxa2
34Rxf5Rb2
35Rb5Kf7
36Rb7+Kf6
37h4g5
38h5Ke5
39Rb6Kd5
40Rxh6Rxb3+
41Kg4Rb2
Matches to:
    e4d5
    exd5
    Nf6
    d4
    Qxd5
    c4
    Qd6
    Nf3c5

结果应该是(代码在移动#之后添加了一个句号,但不是必需的):

1e4 d5 
2exd5 Nf6 
3d4 Qxd5
4c4 Qd6
5Nf3 c5
6Be3 cxd4
7Nxd4 a6
8Be2 e5
9Nf3 Nc6
10O-O Qxd1
11Rxd1 Be7
12Nc3 Be6
13Nd5 Bd8
14Nb6 Rb8
15Ng5 Bf5
16Bf3 e4
17Be2 h6
18Nh3 Bxh3
19gxh3 O-O
20Nd7 Nxd7
21Rxd7 Bf6
22Bf4 Rfd8
23Rad1 Be5
24Bxe5 Rxd7
25Rxd7 Nxe5
26Re7 Nf3+
27Kg2 f5
28Bxf3 exf3+
29Kxf3 Rc8
30b3 b5
31cxb5 axb5
32Rb7 Ra8
33Rxb5 Rxa2
34Rxf5 Rb2
35Rb5 Kf7
36Rb7+ Kf6
37h4 g5
38h5 Ke5
39Rb6 Kd5
40Rxh6 Rxb3+
41Kg4 Rb2

注意,第一步和第五步是错误的,因为它匹配白棋和黑棋。

是什么修改我的正则表达式,使它的工作,使它总是只匹配一边移动?

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
namespace ChessPGNParserConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            string regexStr = @"(?:[PNBRQK]|[a-h][1-8]?x)?[a-h][1-8]|(O(-?O){1,2})['+#]?('s*['!'?]+)?";
            //string regexStr = @"(?:[PNBRQK]|[a-h][1-8]?x)?[a-h][1-8](?:'=[PNBRQK])?(O(-?O){1,2})['+#]?('s*['!'?]+)?";
            //string regexStr =   @"(?:[PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:'=[PNBRQK])?|O(-?O){1,2})['+#]?('s*['!'?]+)?";
            //string regexStr =   @"(?:[PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:'=[PNBRQK])?|O(-?O){1,2})['+#]?('s*['!'?]+)?";
            //string regexStr = @"(?:[PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8])";
            string startsDigitRegexStr = @"^'d*";
            Regex regexpr = new Regex(regexStr);
            Regex regexprDigit = new Regex(startsDigitRegexStr);
            // Read the file and display it line by line.
            System.IO.StreamReader file = new System.IO.StreamReader(@"C:'Users'idf'Documents'My Chess Database'chessgame.txt");
            string replacement = "";
            int moveNumber = 1;
            string line;
            while (null != (line = file.ReadLine()))
            {
                MatchCollection mcDigit = regexprDigit.Matches(line);
                foreach (Match m in mcDigit)
                {
                    line = regexprDigit.Replace(line, replacement);
                    //Console.WriteLine(m);
                }
                //Console.WriteLine(line);
                MatchCollection mc = regexpr.Matches(line);
                int twoMoves = 0;
                Console.Write(moveNumber.ToString() + ". ");
                foreach (Match m in mc)
                {
                    Console.Write(m + " ");
                    if(1 == twoMoves++)
                        Console.WriteLine();
                }
                moveNumber++;
            }
            Console.ReadLine();
        }
    }
}

正则表达式偶尔会给出错误的答案

对于任何实际使用,您将需要长符号(例如解决Nb1d2和Nf3d2之间的歧义)。

但是为什么不重用呢:

https://chessprogramming.wikispaces.com/Algebraic +象棋+符号

未经测试,但试试这个:

(?:[PNBRQK]|[a-h][1-8]?x)?[a-h][1-8](?:'=[PNBRQK])?|O(-?O){1,2})['+#]?('s*['!'?]+)?

推理:

[PNBRQK]|[a-h][1-8]?x

匹配[PNBRQK]x[a-h]x[a-h][1-8]x最多一次,但要求前缀末尾有x。这意味着e4d5的前半部分(e4)将不匹配该组的[a-h][1-8]部分,因为我们需要结束x字符。

[a-h][1-8]

匹配不带前缀的实际移动的内容。这将匹配e4,只匹配e4,使第一行的d5被解释为下一个移动。

您的原始regex在x字符之前过于自由地使用?运算符,因此[a-h]?[1-8]?x?匹配e4,然后[a-h][1-8]匹配d5,导致第一回合的错误输出。最后,我不知道[a-h][1-8]之后的东西是否必要,但我把它留在那里,因为我不知道象棋符号的细节。

为了我们的理解(以及你将来的理解,如果你在不久的将来需要阅读或记录它),请将你的表达分解成更小的部分。你可以让regex生成器做智能扫描表优化。

您还可以构建一些单元测试来测试您的正则表达式扫描器。

您可以稍后对结果进行"OR",而不必担心重复太多。

组合Regexp