使用递归正则在JavaScript中解析SGF格式的技术问询
使用递归正则将SGF转换为JavaScript树形对象
问题背景
SGF是存储围棋对局的文本格式,本质为树形结构的文本编码。由于ECMAScript原生不支持递归正则,尝试XRegExp的matchRecursive函数未得到预期结果,以下是正确的递归正则实现方案。
核心思路
SGF的核心结构是嵌套括号对,递归处理的关键是精准匹配多层嵌套的括号内容。XRegExp的matchRecursive函数专为这类场景设计,之前未达预期大概率是参数配置或内容过滤逻辑有误。
实现步骤
- 剥离外层括号:先移除SGF字符串首尾的最外层括号,聚焦内部节点内容。
- 分割独立节点:利用
matchRecursive匹配分号开头的节点,同时跳过嵌套括号内的内容,避免错误分割。 - 递归解析节点:对每个节点,提取属性键值对,再递归解析其嵌套的子节点,最终组装为JavaScript树形对象。
代码实现
const XRegExp = require('xregexp'); function parseSgf(sgfStr) { // 移除最外层括号 const innerContent = sgfStr.replace(/^\((.*)\)$/, '$1'); // 分割顶层节点:匹配分号开头的节点,忽略嵌套括号内的内容 const topLevelNodes = XRegExp.matchRecursive(innerContent, '\\(', '\\)', 'g', { valueNames: ['text', 'open', 'text', 'close'] }) .filter(item => item.value.startsWith(';') || item.name === 'text') .map(item => item.value.trim()) .filter(Boolean); const parseSingleNode = (nodeStr) => { const node = { properties: {}, children: [] }; // 提取节点属性(如B[aa]) const propertyMatches = nodeStr.match(/([A-Za-z]+)\[([^\]]+)\]/g) || []; propertyMatches.forEach(prop => { const [key, value] = prop.match(/([A-Za-z]+)\[([^\]]+)\]/).slice(1); node.properties[key] = value; }); // 提取子节点的嵌套括号内容 const childNodeMatches = XRegExp.matchRecursive(nodeStr, '\\(', '\\)', 'g', { valueNames: ['text', 'open', 'text', 'close'] }) .filter(item => item.name === 'text') .map(item => item.value.trim()) .filter(Boolean); // 递归解析子节点 node.children = childNodeMatches.map(child => parseSgf(`(${child})`)); return node; }; return topLevelNodes.map(node => parseSingleNode(node)); } // 测试用例 const testSgf = '(;B[aa](;W[ab];B[ac])(;W[ad];B[ae]))'; console.log(JSON.stringify(parseSgf(testSgf), null, 2));
关键说明
matchRecursive的valueNames参数用于区分不同类型的匹配内容,通过过滤可以精准提取节点文本和嵌套子节点内容。- 递归函数
parseSingleNode负责单个节点的属性解析和子节点递归处理,确保树形结构的正确生成。 - 需注意过滤空字符串和无效内容,避免解析过程出现异常。
内容的提问来源于stack exchange,提问作者psygo
相关产品推荐
相关产品推荐

