iOS/Swift环境下基于NSRegularExpression的国际象棋记谱法匹配拆分与解析方案问询
嘿,我之前在做国际象棋记谱解析的时候也碰到过一模一样的痛点——尤其是这种单步和回合走法对的歧义问题,给你分享一套在Swift里用NSRegularExpression搞定的思路,亲测好用:
核心问题拆解
你说的关键点太对了:除了最后一组走法,前面每个回合都是白方+黑方的配对,但单步正则根本没法直接区分,像Nd5e3这种既可能是“马从d5走到e3”的单步,又可能是“白方马到d5+黑方兵到e3”的回合对,纯靠单次正则匹配确实搞不定。
分步解决思路
1. 先拆回合,再拆走法对
我建议先把整个记谱按「回合号+走法串」拆分,比如先把5.Nd5e3e3拆成回合号5和走法串Nd5e3e3,这样我们可以把每个回合的走法单独处理,避免全局匹配的混乱。
2. 针对走法对的正则设计
这里的核心是抓住黑方走法的特征:黑方的兵走法是纯坐标(比如e3),其他棋子会带KQNBR大写字母;而白方走法要么带棋子字母,要么兵走法也是纯坐标,但它一定在黑方走法前面。
我整理了一个适配走法对的正则,先匹配白方走法,再匹配黑方走法(兼容黑方兵的纯坐标):
^(([KQNBR]?[a-h]?[1-8]?[a-h][1-8]))(([KQNBR]?[a-h]?[1-8]?[a-h][1-8])|([a-h][1-8]))$
逻辑很简单:
- 第一组:匹配白方的任意合法走法(不管是带棋子的还是兵的纯坐标)
- 第二部分:匹配黑方走法,要么是带棋子的完整走法,要么是兵的纯坐标
如果怕和回合号混淆,可以加个反向断言确保黑方走法前面不是数字:
^(([KQNBR]?[a-h]?[1-8]?[a-h][1-8]))((?<!\d)([KQNBR]?[a-h]?[1-8]?[a-h][1-8])|([a-h][1-8]))$
3. Swift里的具体实现
分两步走,代码都是可直接复用的:
第一步:提取每个回合的走法串
先把所有回合的结构匹配出来,拿到每个回合的走法内容:
let notation = "5.Nd5e3e3 6.Nf6g4d7 7.Qa1h8" let roundRegex = try! NSRegularExpression(pattern: "(\\d+)\\.([A-Za-z0-9]+)", options: []) let matches = roundRegex.matches(in: notation, options: [], range: NSRange(notation.startIndex..., in: notation)) for match in matches { let roundNumber = notation[Range(match.range(at: 1), in: notation)!] let movePairString = notation[Range(match.range(at: 2), in: notation)!] // 单独处理每个回合的走法对 self.processMovePair(movePairString: String(movePairString), round: String(roundNumber)) }
第二步:拆分走法对为白黑走法
针对每个走法串,用我们的正则拆分,如果匹配失败,就说明是最后一个回合的单步走法(比如黑方认输,只有白方走法):
func processMovePair(movePairString: String, round: String) { let movePairRegex = try! NSRegularExpression(pattern: "^(([KQNBR]?[a-h]?[1-8]?[a-h][1-8]))(([KQNBR]?[a-h]?[1-8]?[a-h][1-8])|([a-h][1-8]))$", options: []) guard let match = movePairRegex.firstMatch(in: movePairString, options: [], range: NSRange(movePairString.startIndex..., in: movePairString)) else { // 处理单步走法的情况 print("回合\(round):仅单步走法 \(movePairString)") return } let whiteMove = movePairString[Range(match.range(at: 1), in: movePairString)!] let blackMove = movePairString[Range(match.range(at: 3), in: movePairString)!] print("回合\(round):白方走法 \(whiteMove),黑方走法 \(blackMove)") }
4. 歧义情况的额外校验
像Nd5e3这种歧义,正则只能帮我们提取候选,最终的正确性还要结合棋局规则判断:
- 如果是单步:校验d5到e3是不是马的合法走法(马走日,坐标差符合(±1,±2)或(±2,±1))
- 如果是走法对:校验白方Nd5和黑方e3是不是各自的合法走法
这部分属于业务逻辑,没法靠正则搞定,但正则已经帮我们把所有可能的候选提取出来了,剩下的校验就简单多了。
总结
这套思路的核心是先拆回合,再拆走法对,把复杂的全局匹配拆成两个简单的步骤,既优雅又容易维护,完全适配iOS/Swift的NSRegularExpression环境。
内容的提问来源于stack exchange,提问作者Magoo

