HTML解析-第二版(C/C++)

这个博客介绍了HTML解析的第二版优化,通过减少内存拷贝显著提升了解析速度。代码已在VS2008环境下测试成功。解析策略是构建一个类似map的数据结构,形成二维单向链表,使用CHtmlObject类处理HTML标记和属性。
摘要由CSDN通过智能技术生成

背景:
基于某些不着边际想法,只为取得HTML页面上的所有“URL”和“文本”,其它的内容都不在关心之列。
问题:
对于“文本”搜索,如果搜索了除英文以外的语言还好说些,如果要搜索的内容是英文本,
那么就难以区分是“标记”还是“本文”了。对于“URL”的搜索,因为“标记”就是英文,
这样就绕回到“对于‘文本’搜索”。另外字母的大小写,被转义的字符,引号,尖括号,都得处理。
例如:
<a href="http://www.csdn.net" >csdn</a>
<script src="http://csdnimg.cn/xxxxxxxx.js" type="text/javascript"></script>
想要搜索“csdn”这个字符串,直接以字符串遍历的法能搜索到3个,其实呢只希望搜索到1个。
例如:
<a href="http://bbs.csdn.net" >论坛</a>
<a href="http://bbs.csdn.net" >论 坛</a>
<a href="http://bbs.csdn.net" >论  坛</a>
想要搜索“论坛”这个字符串,按语义上讲,希望在搜索时能搜到3个。
但直接以字符串遍历的法能搜到1个,原因在于加了“空格”后的字符串,
计算机不知道对于人来讲意思并没有变。

总结:
1:直接搜索特定字符串,不多了就是少了。
2:尝试过MS的COM库,功能强大且齐全,但耗费的资源也相当多。
3:耳熟能详的搜索引擎也跑过几个回合,因没有耐心翻遍所有网页只好放弃。

结论:
只能把HTML页面完整的解析完毕才能达找到想到的东西,尽管不是全部,但情况要好很多。

方法:
HTML语句结构是:<a href="http://www.csdn.net" >aaaa</a> 或 <link href="/favicon.ico" />
等等一连串类似的语句组成,并且只有嵌套没有循环(脚本只能算上面提到的“文本”)。
分界符(这个词本人自己的称呼)使用的是“ <>""''=空格 ”,把两个分界符之间的内容看作一个链表节点,
“标记”a与“标记”/a是“父”节点与“子”节点的关系,“标记”a与“标记”href是“兄弟”节点的关系。
这样的好处是不用关心“标记”含义,就可以把整个页面解析成一个二维链表。
纵向可以遍历“标记”和“文本”,横向可以找到“文本”对应用“URL”。
当然实际情况要复杂的多,种种异常情况都要考虑。如:转意字符,脚本中的括号对称验证等等,
最糟糕是碰到错误的语法,或者根本就不是HTML页面(这个就不属性本文说明范围了)。

                                                                                                                                                               

//以上内容于 2011-12-17  18:01  添加/


1:较“HTML解析-第一版(C/C++)” 减少了内存拷贝,速度相对提高很多。

2:代码在VS2008下测试通过。#define _UNICODE #define _WIN32_WINNT 0x0600

3:解析方法:类似于构建一个map表(STL模板库里的map不利于阅读,可以参考MFC类库的CMap),最终组成一个二维的单向链表。

4:CHtmlObject 类负责解析HTML“标记”和“属性”。


//CHtmlObject.h//

#pragma once

/*****************************************************************************************************************
created: 2011/12/03
author: hmm7e (hmm7e_z@126.com)

*****************************************************************************************************************/

class CHtmlObject
{
public:
//
static BOOL IsSpace(TCHAR tcLetter);
protected:
struct tagNode
{
LPCTSTR s_pszKey;
LPCTSTR s_pszValue;
struct tagNode * s_pstRight; //attribute of tag
struct tagNode * s_pstNext; //next tag
};
public:
CHtmlObject(void);
virtual ~CHtmlObject(void);
//
enum {CHARSET_UTF8,CHARSET_UNICODE,CHARSET_MULTIBYTE}TextCharset;
protected:
//
tagNode * InnerAllocNode();
void InnerFreeNode(tagNode * lpstNode);
void InnerLinkNextNode(tagNode * lpstNode);
void InnerLinkRightNode(tagNode * lpstTagNode,tagNode * lpstNode);
void InnerCleanupNode();
void InnerCleanupRightNode(tagNode * lpstNode);
public:
//
void AutoTakeSnapshot(PBYTE lpszString,UINT nStringLen);
void TakeSnapshot(PBYTE lpszString,UINT nStringLen,UINT nFromCharset );
void DeleteSnapshot();
//
void Parse();
private:
//
void InnerParse();
LPTSTR InnerSplitComment(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitTag(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitContent(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitText(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitScript(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitStyle(tagNode * lpstNode,LPTSTR lpszTagString);

protected:
//
LPTSTR m_pszSnapshotBuffer;
UINT m_nSnapshotBufferLen;
UINT m_nSnapshotStringLen;
//
tagNode * m_pstHead;
tagNode * m_pstTail;

};

//CHtmlObject.h//
//CHtmlObject.cpp//

#pragma once

/*****************************************************************************************************************
created: 2011/12/03
author: hmm7e (hmm7e_z@126.com)

*****************************************************************************************************************/


#include "HtmlObject.h"

//
BOOL CHtmlObject::IsSpace(TCHAR tcLetter)
{
	//以下字符在HTML标记里都算是空格。
	return (tcLetter == _T(' ') || tcLetter == _T('\r') ||tcLetter == _T('\n') ||tcLetter == _T('\t') );
}

CHtmlObject::CHtmlObject(void)
{
	m_pszSnapshotBuffer = NULL;
	m_nSnapshotBufferLen = 0;
	m_nSnapshotStringLen = 0;
	m_pstHead = NULL;
	m_pstTail = NULL;
}

CHtmlObject::~CHtmlObject(void)
{
	DeleteSnapshot();
}
//
CHtmlObject::tagNode * CHtmlObject::InnerAllocNode()
{
	CHtmlObject::tagNode * pstResult = new CHtmlObject::tagNode;
	if( pstResult )
	{
		::ZeroMemory((LPVOID)pstResult,sizeof(CHtmlObject::tagNode));
	}
	return pstResult;
}
void CHtmlObject::InnerFreeNode(CHtmlObject::tagNode * lpstNode)
{
	if( lpstNode )
		delete lpstNode;
}
void CHtmlObject::InnerLinkNextNode(tagNode * lpstNode)
{
	//链接到“尾”结点。
	//1:如果没有“头”节点,那么表示链表是“空”的。
	//2:如果已经存“头”节点,那么就链接新节点到“尾”节点,并重新记录“尾”节点指针。
	if( m_pstHead == NULL )
	{
		m_pstHead = lpstNode;
		m_pstTail = lpstNode;
	}
	else
	{
		m_pstTail->s_pstNext = lpstNode;
		m_pstTail = lpstNode;
	}


#ifdef _DEBUG

	if( lpstNode->s_pszKey )
	{
		::OutputDebugString(_T("--"));
		::OutputDebugString(lpstNode->s_pszKey);
		::OutputDebugString(_T("--\r\n"));
	}
	if( lpstNode->s_pszValue )
	{
		::OutputDebugString(_T("--"));
		::OutputDebugString(lpstNode->s_pszValue);
		::OutputDebugString(_T("--\r\n"));
	}

#endif //_DEBUG

}
void CHtmlObject::InnerLinkRightNode(tagNode * lpstTagNode,tagNode * lpstNode)
{
	//链接到“属性”的“头”节点。
	//1:把现有的“属性”链表,链接到当前新节点的下。
	//2:把当前节点做为“头”节点保存。
	lpstNode->s_pstRight = lpstTagNode->s_pstRight;
	lpstTagNode->s_pstRight
  • 2
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值