先说前面那个“暴力硬编码”,我一直比较反对这种方式,虽然有时真的很好用,但这种解决问题的方式不太符合我编程的习惯--走一步看三步,就是说编写一个对象或者一个函数的时候,要考虑到其未来可能的更改需求,并为可能的需求预留出必要的空间。而硬编码恰恰相反,是以解决当前问题为目标的方法。当然这种方法也没有错误,只是像我这样总是担心未来,悲观的人来说,能不用就不用,因为用了怕睡不着。。。
从十几年前学习HTML4时,就想着写一个HTML解析器,由于认为自己不合适写这种与算法紧密相关的代码,也就一直没有敢动手,怕自己陷进去出不来。这次在分析Lingoes结果时,找不到像tinyxml好用的,轻量的解析库,而硬编码实在有太多的弊端,折腾了几天,最终决定--开始造轮子。。。
基于上面的阐述,就有了下面这个HTML解析对象--TinyHtmlParser。TinyHtmlParser仅仅是分解HTML文档,有些类似tinyxml,但要比其能力差很多,且编码完全没有参考tinyxml,效率也是大问题。此对象对我来说,就是要验证一下是否有能力分解HTML文档,因此功能只是集中在字符串的处理上。TinyHtmlParser由CDocumentObject、CElementObject等简单几个类构成,使用标准且简单的HTML字串测试还算达到预期了,但分析Lingoes的结果时就有些混乱了,对于此问题,不知道是我理解HTML有误,还是浏览器们兼容错误的能力超强。因此后期真对Lingoes的结果做了一些特殊的处理。(真是“源码在手,天下我有”啊。。。)
TinyHtmlParser现在还没有到达可以使用的目标,对我来说,查找某个Tag的value是主要需求,而TinyHtmlParser还没有完成此功能,但这只是时间的问题了。今天着急贴上,是因为要过节了,中间一周不碰电脑,怕自己都忘记现在做什么了,因此记录一下,下次都写好,再慢慢加上为什么会写成这样。
1
#ifndef __TINYHTMLPARSER_H__
2
#define __TINYHTMLPARSER_H__
3![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
4
#include <iostream>
5
#include <string>
6
#include <queue>
7
#include <stack>
8![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
9
namespace TinyHtmlParser
10![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
11![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
12![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
enum ElementType
{ ET_UNKNOWN = -1, ET_TAG = 0, ET_NODE, ET_ELEMENT };//0:just a tag, 1:no value, 2:have value
13![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
14
class CAttributeObject
15![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
16
public:
17
CAttributeObject(const std::string& a, const std::string& v)
18
: attr(a), value(v), next(NULL)
19![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
20
}
21![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
virtual ~CAttributeObject()
{}
22![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
23
public:
24
std::string attr;
25
std::string value;
26
CAttributeObject* next;
27
};
28![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
29
class CElementObject
30![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
31
public:
32
CElementObject();
33
virtual ~CElementObject();
34![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
35
virtual int Analyse();
36![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
37
void Show(std::ostream& os) const;
38
protected:
39
int AnalyseAttribute(const std::string& attr);
40
int MakeAttribute(const std::string& attr);
41
int MakeAttribute(const std::string& attr, const std::string& value);
42
void FreeAnalyseAttribute();
43
int AnalyseValue();
44
public:
45
ElementType type;
46
size_t level;
47
CElementObject* parent;
48
CElementObject* child;
49
CElementObject* sibling;
50![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
51
CAttributeObject* attrib;
52
public:
53
std::string tag;
54
std::string value;
55
};
56![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
57
class CParserData
58![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
59
public:
60![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
enum DataType
{ DT_UNKNOWN = -1, DT_TAG = 0, DT_VALUE, DT_END, DT_DONE, DT_TAG_VALUE };
61
public:
62
CParserData()
63
: type(DT_UNKNOWN)
64![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
65
}
66![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
virtual ~CParserData()
{}
67![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
68
public:
69
DataType type;
70
size_t start;
71
size_t end;
72
size_t vstart;
73
size_t vend;
74
};
75![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
76
class CDocumentObject
77![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
78
protected:
79
static const char TAG_LT = '<';
80
static const char TAG_GT = '>';
81
static const char TAG_SLASH = '/';
82
static const char TAG_BSLASH = '\\';
83
static const char TAG_AND = '&';
84![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
85
typedef std::vector<CParserData> TDataVector;
86![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
87
typedef std::stack<CParserData> TDataStack;
88
struct TNodeData
89![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
90
size_t level;
91
CParserData tag;
92
CParserData value;
93
// CParserData end;
94
};
95
typedef std::deque<TNodeData> TNodeQueue;
96
public:
97
CDocumentObject();
98
virtual ~CDocumentObject();
99![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
100
int Load(const std::string& str);
101![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
102
const CElementObject* Root() const;
103![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
104
const CElementObject* FindTag() const;
105![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
106
void Show(std::ostream& os) const;
107
protected:
108
int PreProcess(const std::string& str, std::string& html);
109
int PreParser(const std::string& html, TNodeQueue& vct);
110
int Parser(const std::string& html, TNodeQueue& que);
111
private:
112
int PreParserLT(const std::string& html, std::string::size_type& pos, CParserData& data);
113
int PushValueData(const CParserData& data, TDataStack& datastack) const;
114
int PushTagData(const std::string& html, const CParserData& data, TDataStack& datatstack, TNodeQueue& nodeque) const;
115
116
int CheckSpecialTag(const std::string& html, const CParserData& data) const;
117
int CheckTag(const std::string& html, const CParserData& tag, const CParserData& end) const;
118
CElementObject* MakeElement(const std::string& html, const TNodeData& node, CElementObject* parent, CElementObject* sibling) const;
119![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
120
void CDocumentObject::ShowElement(std::ostream& os, const CElementObject* e) const;
121![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
122
void FreeElement(CElementObject* root);
123
private:
124
CElementObject* _root;
125
};
126![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
127
}
128![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
129
#endif
130![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
1![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
2
#include "TinyHtmlParser.h"
3![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
4
namespace TinyHtmlParser
5![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
6![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
7
CElementObject::CElementObject()
8
: type(ET_UNKNOWN)
9
, level(0)
10
, parent(NULL)
11
, child(NULL)
12
, sibling(NULL)
13
, attrib(NULL)
14![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
15
}
16![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
17
CElementObject::~CElementObject()
18![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
19
FreeAnalyseAttribute();
20
}
21![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
22
int CElementObject::Analyse()
23![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
24
std::string str = tag;
25![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
26
std::string::size_type pos = str.find(" ");
27
if(pos != std::string::npos)
28![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
29
tag = str.substr(0, pos + 1);
30![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
31
str = str.substr(pos + 1);
32
if(AnalyseAttribute(str) != 0)
33![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
34
return -1;
35
}
36
}
37
if(type == ET_ELEMENT)
38![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
39
if(AnalyseValue() != 0)
40
return -1;
41
}
42
return 0;
43
}
44![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
45
int CElementObject::AnalyseAttribute(const std::string& attr)
46![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
47
if(attr.size() == 0)
48
return 0;
49![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
50
std::string a, v;
51
std::string::size_type pos = attr.find("="), start = 0;
52
while(pos != std::string::npos)
53![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
54
a = attr.substr(start, pos - start);
55
if(pos == attr.size() - 1)
56
return -1;
57
start = pos + 1;
58
if(attr[pos + 1] == '\"')
59![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
60
pos = attr.find("\"", start + 1);
61
if(pos == std::string::npos)
62
return -1;
63
v = attr.substr(start, pos - start + 1);
64
start = pos + 2;
65
}
66
else
67![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
68
pos = attr.find(" ", start);
69
if(pos == std::string::npos)
70
pos = attr.size() - 1;
71
v = attr.substr(start, pos - start);
72
start = pos + 1;
73
}
74
if(MakeAttribute(a, v) != 0)
75
return -1;
76![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
77
if(start >= attr.size())
78
break;
79![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
80
pos = attr.find("=", start);
81
}
82
return 0;
83
}
84![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
85
int CElementObject::MakeAttribute(const std::string &attr)
86![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
87
std::string::size_type pos = attr.find("=");
88
if(pos == std::string::npos)
89
return -1;
90![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
91
return MakeAttribute(attr.substr(0, pos), attr.substr(pos));
92
}
93![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
94
int CElementObject::MakeAttribute(const std::string &attr, const std::string& value)
95![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
96
std::auto_ptr<CAttributeObject> obj(new CAttributeObject(attr, value));//attr.substr(0, pos), attr.substr(pos)));
97
98
if(attrib != NULL)
99![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
100
CAttributeObject* tmp = attrib;
101
while(tmp->next != NULL)
102
tmp = tmp->next;
103
tmp->next = obj.release();
104
}
105
else
106![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
107
attrib = obj.release();
108
}
109
return 0;
110
}
111![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
112![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
113
void CElementObject::FreeAnalyseAttribute()
114![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
115
CAttributeObject* tmp = attrib;
116
while(attrib != NULL)
117![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
118
tmp = attrib->next;
119
delete attrib;
120
attrib = tmp;
121
}
122![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
123
}
124![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
125
int CElementObject::AnalyseValue()
126![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
127
std::string::size_type pos = this->value.find(" ");
128
while(pos != std::string::npos)
129![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
130
this->value.replace(pos, 6, " ");
131
pos = this->value.find(" ", pos + 1);
132
}
133![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
134
return 0;
135
}
136![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
137![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
138
void CElementObject::Show(std::ostream& os) const
139![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
140
os << "[" << this->level << "]" << "Tag:" << this->tag;
141
if(this->type == ET_ELEMENT)
142
os << " -- value:" << this->value;
143
os << std::endl;
144![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
145
const CAttributeObject* attr = this->attrib;
146
while(attr != NULL)
147![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
148
os << " attr:" << attr->attr << " -- value:" << attr->value << std::endl;
149
attr = attr->next;
150
}
151
os << std::endl;
152
}
153
//
154![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
155
CDocumentObject::CDocumentObject()
156
: _root(NULL)
157![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
158
}
159![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
160
CDocumentObject::~CDocumentObject()
161![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
162
if(_root != NULL)
163
FreeElement(_root);
164
}
165![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
166
int CDocumentObject::Load(const std::string &str)
167![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
168
std::string html;
169
if(PreProcess(str, html) != 0)
170
return -1;
171
TNodeQueue que;
172
if(PreParser(html, que) != 0)
173
return -1;
174
if(Parser(html, que) != 0)
175
return -1;
176
return 0;
177
}
178![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
179
int CDocumentObject::PreProcess(const std::string& str, std::string& html)
180![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
181
bool tag = false;
182
for(std::string::const_iterator it = str.begin(); it != str.end(); ++ it)
183![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
184
if(*it == TAG_LT)
185![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
186
if(tag == true)
187
return -1;
188
tag = true;
189
}
190
else if(*it == TAG_GT)
191![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
192
if(tag == false)
193
return -1;
194
tag = false;
195
}
196
else
197![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
198
if(tag == false)
199![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
200
if(isspace((unsigned char)*it) != 0)
201
continue;
202
}
203
}
204
html += *it;
205
}
206![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
207
return 0;
208
}
209![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
210
int CDocumentObject::PreParser(const std::string& html, CDocumentObject::TNodeQueue& que)
211![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
212
std::string::size_type pos = 0;
213![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
214
if(html.size() == 0)
215
return -1;
216
if(html[pos] != TAG_LT)
217
return -1;
218![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
219
TDataStack datastack;
220![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
221
CParserData data;
222![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
223
while(pos < html.size())
224![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
225
if(html[pos] == TAG_LT)
226![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
227
if(pos > data.start)
228![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
229
data.type = CParserData::DT_VALUE;
230
data.end = pos;
231![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
232
// std::cout << "VALUE - " << html.substr(data.start, data.end - data.start) << std::endl;
233
if(PushValueData(data, datastack) != 0)
234
return -1;
235
}
236![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
237
if(PreParserLT(html, pos, data) != 0)
238
return -1;
239
// std::cout << "TAG - " << html.substr(data.start, data.end - data.start) << std::endl;
240
if(PushTagData(html, data, datastack, que) != 0)
241
return -1;
242![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
243
++ pos;
244
data.start = pos;
245
}
246
//else if(html[pos] == TAG_GT || html[pos] == TAG_SLASH)
247
//{
248
// return -1;
249
//}
250
else
251![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
252
++ pos;
253
}
254
// std::cout << (char)html[pos] << std::endl;
255
}
256![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
257
return 0;
258
}
259![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
260
int CDocumentObject::Parser(const std::string& html, CDocumentObject::TNodeQueue& que)
261![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
262
CElementObject *pe = NULL, *pp = NULL, *ps = NULL;
263
size_t level = 0;
264
while(que.size()> 0)
265![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
266
const TNodeData &node = que.front();
267
if(level < que.front().level)
268![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
269
pp = pe;
270
ps = NULL;
271
}
272
else if(level == que.front().level)
273![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
274
ps = pe;
275
}
276
else//>
277![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
278
ps = pe;
279
pp = pe->parent;
280
int t = level - que.front().level;
281
while(t > 0)
282![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
283
ps = ps->parent;
284
pp = pp->parent;
285
-- t;
286
}
287
}
288
level = que.front().level;
289![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
290
pe = MakeElement(html, que.front(), pp, ps);
291![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
292
if(pe == NULL)
293
return -1;
294![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
295
que.pop_front();
296
}
297![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
298
if(pp != NULL)
299![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
300
while(pp->parent != NULL)
301
pp = pp->parent;
302
_root = pp;
303
}
304
else
305
_root = pe;
306![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
307
return 0;
308
}
309![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
310
int CDocumentObject::PreParserLT(const std::string& html, std::string::size_type& pos, CParserData& data)
311![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
312
if(pos == html.size() - 1)
313
return -1;
314![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
315
data.start = pos;
316![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
317
++ pos;
318![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
319
if(html[pos] != TAG_SLASH)
320![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
321
data.type = CParserData::DT_TAG;
322
}
323
else
324![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
325
data.type = CParserData::DT_END;
326
++ pos;
327
}
328![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
329
while(pos < html.size())
330![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
331
if(html[pos] == TAG_GT)
332![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
333
if(html[pos - 1] == TAG_SLASH)
334![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
335
data.type = CParserData::DT_DONE;
336
}
337![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
338
data.end = pos;
339
340
return 0;
341
}
342
else if(html[pos] == TAG_LT)
343![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
344
return -1;
345
}
346![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
347
++ pos;
348
}
349![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
350
return -1;
351
}
352![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
353
int CDocumentObject::PushValueData(const TinyHtmlParser::CParserData &data, CDocumentObject::TDataStack &datastack) const
354![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
355
if(datastack.size() == 0)
356
return -1;
357
datastack.push(data);
358
return 0;
359
}
360![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
361
int CDocumentObject::PushTagData(const std::string& html, const CParserData& data, CDocumentObject::TDataStack& datastack, CDocumentObject::TNodeQueue& nodeque) const
362![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
363
if(data.type == CParserData::DT_TAG)
364![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
365
if(CheckSpecialTag(html, data) == 0)
366![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
367
TNodeData node;
368
node.tag = data;
369![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
370
node.level = datastack.size();
371
nodeque.push_front(node);
372
return 0;
373
}
374![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
375
if(datastack.size() > 0 && datastack.top().type == CParserData::DT_VALUE)
376![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
377
CParserData data = datastack.top();
378
datastack.pop();
379
if(datastack.top().type != CParserData::DT_TAG)
380
return -1;
381
datastack.top().type = CParserData::DT_TAG_VALUE;
382
datastack.top().vstart = data.start;
383
datastack.top().vend = data.end;
384
}
385![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
386
datastack.push(data);
387
}
388
else if(data.type == CParserData::DT_END)
389![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
390
if(datastack.size() == 0)
391
return -1;
392![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
393
TNodeData node;
394
if(datastack.top().type == CParserData::DT_TAG || datastack.top().type == CParserData::DT_TAG_VALUE)
395![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
396
node.tag = datastack.top();
397
datastack.pop();
398
}
399
else if(datastack.top().type == CParserData::DT_VALUE)
400![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
401
node.value = datastack.top();
402![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
403
// std::cout << "value - " << html.substr(node.value.start, node.value.end - node.value.start) << std::endl;
404![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
405
datastack.pop();
406
407
if(datastack.size() == 0)
408
return -1;
409![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
410
if(datastack.top().type == CParserData::DT_TAG)
411![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
412
node.tag = datastack.top();
413
}
414
else if(datastack.top().type == CParserData::DT_TAG_VALUE)
415![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
416
node.tag = datastack.top();
417
}
418
else
419![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
420
return -1;
421
}
422![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
423
//node.tag = datastack.top();
424
//else if(datastack.top().type == CParserData::DT_TAG_VALUE)
425
//{
426
// node.tag = datastack.top();
427
//}
428
datastack.pop();
429
}
430
else
431![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
432
// std::cout << "type : " << datastack.top().type << std::endl;
433
return -1;
434
}
435![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
436
if(CheckTag(html, node.tag, data) != 0)
437
return -1;
438![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
439
node.level = datastack.size();
440
nodeque.push_front(node);
441
}
442
else if(data.type == CParserData::DT_DONE)
443![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
444
if(datastack.size() > 0 && datastack.top().type == CParserData::DT_VALUE)
445![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
446
CParserData data = datastack.top();
447
datastack.pop();
448
if(datastack.top().type != CParserData::DT_TAG)
449
return -1;
450
datastack.top().type = CParserData::DT_TAG_VALUE;
451
datastack.top().vstart = data.start;
452
datastack.top().vend = data.end;
453
}
454![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
455
// datastack.push(data);
456![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
457
TNodeData node;
458
node.tag = data;
459![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
460
node.level = datastack.size();
461
nodeque.push_front(node);
462
}
463
else
464![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
465
return -1;
466
}
467
return 0;
468
}
469![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
470
int CDocumentObject::CheckSpecialTag(const std::string& html, const CParserData& data) const
471![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
472
std::string tag = html.substr(data.start + 1, data.end - data.start - 1);
473
std::string::size_type pos = tag.find(" ");
474
if(pos != std::string::npos)
475
tag = tag.substr(0, pos);
476
477
if(tag == "IMG")
478
return 0;
479![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
480
return -1;
481
}
482![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
483
int CDocumentObject::CheckTag(const std::string& html, const CParserData& tag, const CParserData& end) const
484![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
485
std::string str = html.substr(tag.start + 1, tag.end - tag.start - 1);
486
std::string::size_type pos = str.find(" ");
487
if(pos != std::string::npos)
488
str = str.substr(0, pos);
489
490
if(str != html.substr(end.start + 2, end.end - end.start - 2))
491![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
492
// std::cout << "tag : " << str << " -- end : " << html.substr(end.start + 2, end.end - end.start - 2) << std::endl;
493
return -1;
494
}
495
return 0;
496
}
497![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
498
CElementObject* CDocumentObject::MakeElement(const std::string& html, const CDocumentObject::TNodeData &node, CElementObject *parent, CElementObject *sibling) const
499![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
500
std::auto_ptr<CElementObject> ele(new CElementObject);
501
502
ele->level = node.level;
503![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
504
if(node.tag.type == CParserData::DT_TAG)
505![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
506
ele->type = ET_NODE;
507
ele->tag = html.substr(node.tag.start + 1, node.tag.end - node.tag.start - 1);
508
}
509
else if(node.tag.type == CParserData::DT_DONE)
510![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
511
ele->type = ET_TAG;
512
ele->tag = html.substr(node.tag.start + 1, node.tag.end - node.tag.start - 2);
513
}
514
else if(node.tag.type == CParserData::DT_TAG_VALUE)
515![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
516
ele->tag = ET_NODE;
517
ele->tag = html.substr(node.tag.start + 1, node.tag.end - node.tag.start - 1);
518
}
519
else
520
return NULL;
521![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
522
if(node.value.type == CParserData::DT_VALUE)
523![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
524
ele->type = ET_ELEMENT;
525
if(node.tag.type == CParserData::DT_TAG)
526
ele->value = html.substr(node.value.start, node.value.end - node.value.start);
527
else
528
ele->value = html.substr(node.tag.vstart, node.tag.vend - node.tag.vstart) + "%" + html.substr(node.value.start, node.value.end - node.value.start);
529
}
530![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
531
if(ele->Analyse() != 0)
532![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
533
return NULL;
534
}
535![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
536
if(parent != NULL)
537
parent->child = ele.get();
538
ele->parent = parent;
539
ele->sibling = sibling;
540![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
541
//std::cout << "element: tag - " << ele->tag << std::endl;
542![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
543
return ele.release();
544
}
545![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
546
void CDocumentObject::Show(std::ostream &os) const
547![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
548
if(_root != NULL)
549
ShowElement(os, _root);
550
}
551![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
552
void CDocumentObject::ShowElement(std::ostream& os, const CElementObject* e) const
553![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
554
const CElementObject* pe = e, *ps = e->sibling;
555![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
556
pe->Show(os);
557
558
pe = pe->child;
559
if(pe != NULL)
560![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
561
ShowElement(os, pe);
562
}
563
if(ps != NULL)
564![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
565
ShowElement(os, ps);
566
}
567
}
568![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
569
void CDocumentObject::FreeElement(CElementObject* root)
570![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
571
CElementObject* pe = root->child, *ps = root->sibling;
572![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
573
// std::cout << "free:" << root->tag << std::endl;
574![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
575
if(root != NULL)
576![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
577
free(root);
578
root = NULL;
579
}
580![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
581
if(pe != NULL)
582![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
583
FreeElement(pe);
584
}
585
if(ps != NULL)
586![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
587
FreeElement(ps);
588
}
589
}
590![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
591
}
下面是测试代码,其中有直接copy自lingoes的结果串。
1
#include <iostream>
2
#include <string>
3![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
4
#include "TinyHtmlParser.h"
5![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
6
using namespace TinyHtmlParser;
7![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
8
int Test()
9![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
10
//std::string str = "<hh></hh><HTML>\n<DOC DFDK = \"DD\"/><BODY> 1 2 3 </BODY><abc><def></def></abc></HTML><TEST>567</TEST>";
11![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
12
std::string str = \
13
"<DIV id=main dir=ltr style=\"FONT-SIZE: 9pt; FONT-FAMILY: Tahoma, Arial; HEIGHT: 100%\">"
14
"<DIV id=main_info style=\"DISPLAY: none\"> </DIV>"
15
"<DIV id=main_wnd>"
16
"<DIV id=lingoes_dictarea></DIV>"
17
"<DIV id=dict_E1C27E806399D047822B6650194A3D32 style=\"PADDING-RIGHT: 10px; PADDING-LEFT: 10px; FONT-SIZE: 10.5pt; PADDING-BOTTOM: 0px; WIDTH: 100%; LINE-HEIGHT: 1.2em; PADDING-TOP: 10px; FONT-FAMILY: 'Tahoma'\" groupid=\"2\" dictid=\"E1C27E806399D047822B6650194A3D32\">"
18
"<TABLE onselectstart=\"return true\" id=dict_head_E1C27E806399D047822B6650194A3D32 cellSpacing=0 cellPadding=0 border=0>"
19
"<TBODY>"
20
"<TR>"
21
"<TD style=\"BORDER-RIGHT: #92b0dd 1px solid; BORDER-TOP: #92b0dd 1px solid; FONT-SIZE: 9pt; BACKGROUND: #cfddf0; BORDER-LEFT: #92b0dd 1px solid; COLOR: #000080; LINE-HEIGHT: 1em; BORDER-BOTTOM: #92b0dd 1px solid; FONT-FAMILY: ''\" noWrap>"
22![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
"<DIV onmouseup=\"this.className='btn2_mouse_up'\" class=btn2_mouse_out onmousedown=\"this.className='btn2_mouse_down'\" id=dict_title_E1C27E806399D047822B6650194A3D32 onmouseover=\"this.className='btn2_mouse_over'\" title=\"Dictionary Menu\" style=\"MARGIN: 0px 3px 1px 0px\" onclick=\"window.navigate('app://dictmenu/E1C27E806399D047822B6650194A3D32-2')\" onmouseout=\"this.className='btn2_mouse_out'\"><IMG height=16 hspace=3 src=\"file:/**////D:/Profiles/grp436/Local%20Settings/Application%20Data/Lingoes/Translator/temp/dict/E1C27E806399D047822B6650194A3D32/icon.png\" width=16 align=absMiddle border=0><SPAN style=\"PADDING-RIGHT: 4px; PADDING-LEFT: 2px; PADDING-BOTTOM: 0px; PADDING-TOP: 0px\">Vicon English-Chinese(S) Dictionary</SPAN><IMG height=4 src=\"file:///C:/Program%20Files/Lingoes/Translator2/dict/image/menu.png\" width=7 align=absMiddle border=0> </DIV></TD>"
23![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
"<TD style=\"BORDER-BOTTOM: #92b0dd 1px solid\"><IMG style=\"DISPLAY: none\" height=8 hspace=5 src=\"file:/**////C:/Program%20Files/Lingoes/Translator2/dict/image/sst.png\" width=11 align=absMiddle border=0> </TD>"
24
"<TD style=\"BORDER-BOTTOM: #92b0dd 1px solid\" align=right width=\"100%\">"
25![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
"<DIV style=\"OVERFLOW: hidden; WIDTH: 10px; CURSOR: hand; MARGIN-RIGHT: 2px; HEIGHT: 10px\"><IMG onmouseup=\"this.style.marginLeft = '-10px'\" onmousedown=\"this.style.marginLeft = '-10px'\" id=dict_show_E1C27E806399D047822B6650194A3D32 onmouseover=\"this.style.marginLeft = '-10px'\" title=\"Minimize Result\" style=\"MARGIN-TOP: 0px; MARGIN-LEFT: 0px\" onclick=\"window.navigate('app://hidemeaning/E1C27E806399D047822B6650194A3D32-2') ; this.style.marginTop = (parseInt(this.style.marginTop) == 0) ? '-10px' : '0px' ;\" onmouseout=\"this.style.marginLeft = '0px'\" height=20 src=\"file:/**////C:/Program%20Files/Lingoes/Translator2/dict/image/expand.png\" width=20 border=0></DIV></TD></TR></TBODY></TABLE>"
26
"<DIV id=dict_body_E1C27E806399D047822B6650194A3D32>"
27
"<DIV id=dict_gls_E1C27E806399D047822B6650194A3D32>"
28
"<DIV style=\"MARGIN: 5px 0px\">"
29
"<DIV style=\"WIDTH: 100%\">"
30![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
"<DIV style=\"FLOAT: left; LINE-HEIGHT: normal\"><IMG height=11 src=\"file:/**////C:/Program%20Files/Lingoes/Translator2/dict/image/entry_p.png\" width=10 align=absMiddle border=0> </DIV>"
31
"<DIV style=\"OVERFLOW-X: hidden; WIDTH: 100%\">"
32
"<DIV style=\"MARGIN: 0px 0px 5px; COLOR: #808080; LINE-HEIGHT: normal\"><SPAN style=\"FONT-SIZE: 10.5pt; COLOR: #000000; LINE-HEIGHT: normal\"><B>what</B></SPAN> <SPAN style=\"FONT-SIZE: 10.5pt; LINE-HEIGHT: normal; FONT-FAMILY: 'Lingoes Unicode'\">[<FONT color=#009900>hwɑt ,hw?t /w?t</FONT>]</SPAN></DIV>"
33
"<DIV style=\"MARGIN: 0px 0px 5px\">"
34
"<DIV style=\"MARGIN: 4px 0px\"><FONT color=#c00000>adj.</FONT> 什么</DIV></DIV>"
35
"<DIV style=\"MARGIN: 0px 0px 5px\">"
36
"<DIV style=\"MARGIN: 4px 0px\"><FONT color=#c00000>adv.</FONT> 到什么程度, 在哪一方面</DIV></DIV>"
37
"<DIV style=\"MARGIN: 0px 0px 5px\">"
38
"<DIV style=\"MARGIN: 4px 0px\"><FONT color=#c00000>interj.</FONT> 怎么</DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV>";
39![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
40
//std::string str = \
41
// "<1 hello><1-1>[<1-1-1/>]</1-1><1-2/><1-3/></1><2/><3><3-1><3-1-1/></3-1></3>";
42![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
43
CDocumentObject doc;
44
doc.Load(str);
45![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
46
doc.Show(std::cout);
47![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
48
return 0;
49
}
50![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
51![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
52
int main()
53![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
54
Test();
55
return 0;
56
}