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

时间:2014-08-08 18:16:13

标签: c# regex

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

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

[我不介意使用短代数表示法(SAN)的C#YACC词法分析器,但我现在正在使用正则表达式:]

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

3 个答案:

答案 0 :(得分:0)

未经测试,但试试这个:

(?:[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。这意味着e4d5e4)的前半部分与该组的[a-h][1-8]部分不匹配,因为我们需要结束x字符。

[a-h][1-8]

匹配没有前缀的实际移动内容。这将匹配e4并且仅匹配e4,将第一行中的d5保留为下一步行。

您的原始正则表达式在?字符之前使用x运算符过于宽松,因此[a-h]?[1-8]?x?匹配e4然后[a-h][1-8]匹配{ {1}},导致第一圈的输出错误。最后,我不知道d5之后的内容是否有必要,但我把它留在那里,因为我不知道国际象棋符号的细节。

答案 1 :(得分:0)

对于任何实际用途,您需要长记法(例如,解决Nb1d2和Nf3d2之间的歧义)。

但为什么不重用:

https://chessprogramming.wikispaces.com/Algebraic+Chess+Notation

答案 2 :(得分:0)

请为我们的理解(以及将来,如果您需要在不久的将来阅读或记录它),请将您的表达分解成小块。 您可以让正则表达式构建器执行智能扫描表优化。

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

您可以稍后“删除”结果,而不必担心重复过多。

与以下链接类似:Combine Regexp