最近参加了一个面试,和面试官聊的很好,但是最后的算法题让我有点尴尬了——第一反应是没思路,大概是对XML格式和对DOM树的理解还未到位,面试结束后,经过网上资料搜索和自己的学习,分析了以下算法题目:
综合以上题目,我就直接上代码分析了:
function parseXML(xmlString) {
const stack = [];//用于存储解析过程中的节点
let currentNode = null;//当前节点
let tagName = "";//当前标签名
let textContent = "";//当前文本内容
for (let i = 0; i < xmlString.length; i++) {
if (xmlString[i] === "<") {
// 开始标签
if (xmlString[i + 1] !== "/") {
// 获取标签名
const nextSpaceIndex = xmlString.indexOf(" ", i + 1);
const nextCloseIndex = xmlString.indexOf(">", i + 1);
const endIndex = Math.min(nextSpaceIndex, nextCloseIndex);
tagName = xmlString.substring(i + 1, endIndex);
//在找到空格和大于号之后,使用substring方法提取标签名
// 创建节点
const node = { tagName, attributes: {}, children: [], parent: currentNode };
stack.push(node);
currentNode = node;
//创建了一个节点对象,该节点的属性包括'tagName'、'attributes'、'children'、'parent',然后将该节点对象推入栈中,并将当前节点更新为新创建的节点
// 解析属性
let attrName = "";//用于暂存属性名
let attrValue = "";//用于暂存属性值
for (let j = endIndex + 1; j < xmlString.length; j++) {
if (xmlString[j] === "=") {
attrName = xmlString.substring(endIndex + 1, j);//当遇到“=”时,则将“attrName”更新为等号前的字符串,即属性名。
}
if (xmlString[j] === "\"" || xmlString[j] === "'") {
const nextQuoteIndex = xmlString.indexOf(xmlString[j], j + 1);
attrValue = xmlString.substring(j + 1, nextQuoteIndex);
j = nextQuoteIndex;
currentNode.attributes[attrName] = attrValue;
attrName = "";
attrValue = "";
}
//如果遇到单引号或双引号,则表示属性值的开始和结束,通过查找下一个相同的引号位置,提取出属性值,并将属性名和属性值存储在当前节点的attributes对象中
if (xmlString[j] === ">") {
break;
}//解析完成
endIndex = j;
}
}
// 结束标签
else {
currentNode.textContent = textContent.trim();
textContent = "";
currentNode = stack.pop();
}
} else {
textContent += xmlString[i];
}
}
return currentNode;
}
这个算法主要是使用递归和栈结构来实现解析XML字符串并构建DOM树,不过这个算法只适用于结构较为简单的XML文本,限制条件如下:
(1)合乎XML语法,不包含注释或者处理指令
(2)标签或属性名只包含字母、数字和下划线,且不以数字开头
(3)属性值只包含双引号或单引号