我想问一下将一个简单的html代码解析为DOM节点树的最佳方法是什么:
以下是我面临的一些限制因素:
<p>
,<h1>
,<a>
等标签之间可以有文字。我在考虑正则表达式,但从未尝试过这样做..有什么想法吗?
树中的每个节点都是这个结构:
typedef struct tag
{
struct tag* parent;
struct tag* nextSibling;
struct tag* previousSibling;
struct tag* firstChild;
struct tag* lastChild;
char* name;
char* text;
}node;
答案 0 :(得分:2)
我知道它不在C中,但该演示文稿可能会为您提供有关如何有效解决问题的一些意见。
我还在JavaScript中编写了一个非常简单的解析器示例(同样不在C中,但希望您也知道JS),这是基于您的初始需求,这意味着它不会解析任何属性,也不会处理自动关闭标记和根据HTML规范应该处理的许多其他事情。它将以这种格式生成一个解析树:
{
cn: [{
tag: 'html',
cn: [{
tag: 'body',
cn: [
{ tag: 'h1', cn: ['test'] },
' some text ',
...
]
}]
}]
}
以下是代码和小提琴:http://jsfiddle.net/LUpyZ/3/
请注意,不会忽略空白区域,并且会在文本节点中捕获空白区域。
var html = '<html><body><h1>test</h1> some text <div> <p>text</p></div></body></html>';
var parseHTML = (function () {
var nodesStack = [],
i = 0,
len = html.length,
stateFn = parseText,
parseTree = { cn: [] },
alphaNumRx = /\w/,
currentNode = parseTree,
text = '',
tag = '',
newNode;
function parseTag(token) {
if (token === '/') {
return parseCloseTag;
}
i--; //backtrack to first tag character
return parseOpenTag;
}
function parseCloseTag(token) {
if (token === '>') {
if (currentNode.tag !== tag) {
throw 'Wrong closed tag at char ' + i;
}
tag = '';
nodesStack.pop();
currentNode = currentNode.parentNode;
return parseText;
}
assertValidTagNameChar(token);
tag += token;
return parseCloseTag;
}
function parseOpenTag(token) {
if (token === '>') {
currentNode.cn.push(newNode = { tag: tag, parentNode: currentNode, cn: []});
nodesStack.push(currentNode = newNode);
tag = '';
return parseText;
}
assertValidTagNameChar(token);
tag += token;
return parseOpenTag;
}
function parseText(token) {
if (token === '<') {
if (text) {
currentNode.cn.push(text);
text = '';
}
return parseTag;
}
text += token;
return parseText;
}
function assertValidTagNameChar(c) {
if (!alphaNumRx.test(c)) {
throw 'Invalid tag name char at ' + i;
}
}
return function (html) {
for (; i < len; i++) {
stateFn = stateFn(html[i]);
}
if (currentNode = nodesStack.pop()) {
throw 'Unbalanced tags: ' + currentNode.tag + ' is never closed.';
}
return parseTree;
};
})();
console.log(parseHTML(html));