基本的HTML文本解析器的设计和实现C++源码图文并茂Word格式文档下载.docx
《基本的HTML文本解析器的设计和实现C++源码图文并茂Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《基本的HTML文本解析器的设计和实现C++源码图文并茂Word格式文档下载.docx(25页珍藏版)》请在冰豆网上搜索。
为了再次简化编程模型,我(liigo)继续将“开始标签”“结束标签”“普通文字”三者统一抽象归纳为“节点”(HtmlNode),相应的,“节点”有三种类型,要么是开始标签,要么是结束标签,要么是普通文字。
现在,HTML在我们眼里更加单纯了,它就是“节点”的线性顺序组合,是一维的“节点”数组。
如下图所示:
HTML文本=节点1+节点2+节点3+……
在正式编码之前,先确定好“节点”的数据结构。
作为“普通文字”节点,需要记录一个文本(text);
作为“标签”节点,需要记录标签名称(tagName)、标签类型(tagType)、所有属性值(props);
另外还要有个类型(type)以便区分该节点是普通文字、开始标签还是结束标签。
这其中固然有些冗余信息,比如对标签来说不需要记录文本,对普通文字来说又不需要记录标签名称、属性值等,不过无伤大雅,简洁的编程模型是最大的诱惑。
用C/C++语言语法表示如下:
viewplaincopytoclipboardprint?
1.enum
HtmlNodeType
2.{
3.
NODE_UNKNOWN
=
0,
4.
NODE_START_TAG,
5.
NODE_CLOSE_TAG,
6.
NODE_CONTENT,
7.};
8.enum
HtmlTagType
9.{
10.
TAG_UNKNOWN
11.
TAG_A,
TAG_DIV,
TAG_FONT,
TAG_IMG,
TAG_P,
TAG_SPAN,
TAG_BR,
TAG_B,
TAG_I,
TAG_HR,
12.};
13.struct
HtmlNodeProp
14.{
15.
WCHAR*
szName;
16.
szValue;
17.};
18.#define
MAX_HTML_TAG_LENGTH
(15)
19.struct
HtmlNode
20.{
21.
type;
22.
tagType;
23.
WCHAR
tagName[MAX_HTML_TAG_LENGTH+1];
24.
text;
25.
int
propCount;
26.
HtmlNodeProp*
props;
27.};
具体到编写程序代码,要比想象中容易的多。
编码的核心要点是,以左右尖括号(<
)为边界自然分割标签和普通文字。
左右尖括号之间的当然是标签节点(开始标签或结束标签),左尖括号(<
)之前(直到前一个右尖括号或开头)、右尖括号(>
)之后(直到后一个左尖括号或结尾)的显然是普通文字节点。
区分开始标签或结束标签的关键点是,看左尖括号(<
)后面第一个非空白字符是否为'
/'
。
对于开始标签,在标签名称后面,间隔至少一个空白字符,可能会有形式为“key1=value1key2=value2key3”的属性表,关于属性表,后文有专门的函数负责解析。
此外有一点要注意,属性值一般有引号括住,引号内出现的左右尖括号应该不被视为边界分隔符。
下面就是负责把HTML文本解析为一个个节点(HtmlNode)的核心代码(不足百行,够精简吧):
1.void
HtmlParser:
:
ParseHtml(const
szHtml)
m_html
szHtml
?
L"
"
;
freeHtmlNodes();
if(szHtml
==
NULL
||
*szHtml
L'
\0'
)
return;
p
(WCHAR*)
szHtml;
7.
s
8.
HtmlNode*
pNode
NULL;
9.
c;
bool
bInQuotes
false;
while(
c
*p
12.
{
13.
if(c
\"
'
14.
!
bInQuotes;
p++;
continue;
17.
}
18.
if(bInQuotes)
19.
20.
<
if(p
>
s)
//Add
Text
Node
27.
NewHtmlNode();
28.
pNode->
type
NODE_CONTENT;
29.
text
duplicateStrUtill(s,
true);
30.
31.
+
1;
32.
33.
else
34.
35.
36.
37.
HtmlTag
38.
39.
while(isspace(*s))
s++;
40.
(*s
NODE_START_TAG
NODE_CLOSE_TAG);
41.
if(*s
42.
copyStrUtill(pNode->
tagName,
MAX_HTML_TAG_LENGTH,
s,
43.
//处理自封闭的结点,
如
br/>
删除tagName中可能会有的'
字符
44.
//自封闭的结点的type设置为NODE_START_TAG应该可以接受(否则要引入新的NODE_STARTCLOSE_TAG)
45.
tagNamelen
wcslen(pNode->
tagName);
46.
if(pNode->
tagName[tagNamelen-1]
47.
48.
//处理结点属性
49.
for(int
i
0;
tagNamelen;
i++)
50.
51.
tagName[i]
//第一个空格后面跟的是属性列表
52.
='
//扩展支持这种格式:
tagName=value>
等效于<
tagName
53.
54.
55.
props
(pNode->
1
s);
56.
duplicateStrUtill(props,
57.
nodeTextLen
text);
58.
text[nodeTextLen-1]
//去掉最后可能会有的'
字符,
如这种情况:
img
src="
..."
mce_src="
/>
59.
60.
parseNodeProps(pNode);
//parse
61.
break;
62.
63.
64.
ta