正则表达式偶尔会给出错误的答案
本文关键字:错误 答案 出错 偶尔 正则表达式 | 更新日期: 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