C/C++解析 HTML

本文在HMM7E的博文http://blog.csdn.net/hmm7e/article/details/7071705  HTML解析-第二版(C/C++)上修正了小部分错误,感谢他给予我的帮助

经修改后的工具类以及使用类大家可直接使用

基于某些不着边际想法,只为取得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页面完整的解析完毕才能达找到想到的东西,尽管不是全部,但情况要好很多。

思路:

1.初步分析所有的HTML标签,将其分出必要的层次;

2.层次较高的标签可以对象化,而层次低一些的标签或元素按照用途及使用频率分别对待;

3.凡对象化的标签,在C语言中都可以声明为结构,未对象化的元素可以按性质以相应的数据类型进行存储。

要求:

1.不是所有的标签和元素都可以对象化的,那样会非常复杂;

2.所有被对象化的标签都必须写一套专用的解释和处理程序;

3.上述思路适合数据库的存储。

方法:

HTML语句结构是:<a href="http://www.csdn.net" >aaaa</a> 或 <link href="/favicon.ico" />

等等一连串类似的语句组成,并且只有嵌套没有循环(脚本只能算上面提到的“文本”)。

分界符(这个词本人自己的称呼)使用的是“ <>""‘‘=空格 ”,把两个分界符之间的内容看作一个链表节点,

“标记”a与“标记”/a是“父”节点与“子”节点的关系,“标记”a与“标记”href是“兄弟”节点的关系。

这样的好处是不用关心“标记”含义,就可以把整个页面解析成一个二维链表。

纵向可以遍历“标记”和“文本”,横向可以找到“文本”对应用“URL”。

当然实际情况要复杂的多,种种异常情况都要考虑。如:转意字符,脚本中的括号对称验证等等,

最糟糕是碰到错误的语法,或者根本就不是HTML页面(这个就不属性本文说明范围了)。

还是一样

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

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

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

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

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

#ifndef __JESONYANG_HTMLOBJECT_H__
#define __JESONYANG_HTMLOBJECT_H__

/*****************************************************************************************************************
created: 2011/12/03
author: JesonYang
blog: http://blog.csdn.net/yc7369
*****************************************************************************************************************/
#include <windows.h>
#include <tchar.h>
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();				                                //分配新的html节点
	void InnerFreeNode(tagNode * lpstNode);								    //删除html节点
	void InnerLinkNextNode(tagNode * lpstNode);                             //下一个html节点
	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;

};
#endif
//////////////////////////////////////////////////////////////////////////////////////////CHtmlObject.h//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////CHtmlObject.cpp//////////////////////////////////////////////////author: JesonYang////////////////////////

#pragma once

/*****************************************************************************************************************
created: 2011/12/03
author: JesonYang
blog: http://blog.csdn.net/yc7369
*****************************************************************************************************************/

#include "HtmlObject.h"
#include "HtmlHelper.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 = 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::InnerCleanupNode()
{
	//循环清除所有节点。如果存在“属性”节点一并清除。
	CHtmlObject::tagNode * pstPrev = NULL;
	while (m_pstHead)
	{
		pstPrev = m_pstHead;
		m_pstHead = m_pstHead->s_pstNext;
		//first
		InnerCleanupRightNode(pstPrev);
		//second
		InnerFreeNode(pstPrev);
	}
	m_pstHead = NULL;
	m_pstTail = NULL;
}
void CHtmlObject::InnerCleanupRightNode(CHtmlObject::tagNode * lpstNode)
{
	//循环清除所有“属性”节点。
	CHtmlObject::tagNode * pstHead = lpstNode->s_pstRight;
	CHtmlObject::tagNode * pstPrev = NULL;
	while (pstHead)
	{
		pstPrev = pstHead;
		pstHead = pstHead->s_pstRight;
		InnerFreeNode(pstPrev);
	}
	pstHead = NULL;
	pstPrev = NULL;
}
//
void CHtmlObject::AutoTakeSnapshot(PBYTE lpszString, UINT nStringLen)
{

	if (lpszString && nStringLen > 0)
	{
		//根据数据头自动判断是否需要转换数据到当前应程所使用的编码。
		if (nStringLen >= 2)
		{
			if (lpszString[0] == 0xFF && lpszString[1] == 0xFE) // skip 0xFF,0xFE
			{
				TakeSnapshot(lpszString + 2, nStringLen - 2, CHtmlObject::CHARSET_UNICODE);
			}
			else if (lpszString[0] == 0xEF && lpszString[1] == 0xBB && lpszString[2] == 0xBF)// skip 0xEF,0xBB,0xBF
			{
				TakeSnapshot(lpszString + 3, nStringLen - 3, CHtmlObject::CHARSET_UTF8);
			}
			else
			{
				TakeSnapshot(lpszString, nStringLen, CHtmlObject::CHARSET_MULTIBYTE);
			}
		}
		else
		{
			TakeSnapshot(lpszString, nStringLen, CHtmlObject::CHARSET_MULTIBYTE);
		}
	}
}
void CHtmlObject::TakeSnapshot(PBYTE lpszString, UINT nStringLen, UINT nFromCharset)
{
	//delete old snapshot
	DeleteSnapshot();

	if (lpszString && nStringLen > 0)
	{

		//transform to TCHAR

		if (CHtmlHelper::CHARSET_UTF8 == nFromCharset)
		{

#ifdef _UNICODE 

			m_nSnapshotBufferLen = nStringLen;
			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];

			::memset((LPVOID)m_pszSnapshotBuffer, 0, m_nSnapshotBufferLen*sizeof(TCHAR));
			m_nSnapshotStringLen = ::MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpszString, nStringLen, m_pszSnapshotBuffer, m_nSnapshotBufferLen);
#else
			::OutputDebugString(_T("no support"));

#endif //_UNICODE

		}
		else if (CHtmlHelper::CHARSET_UNICODE == nFromCharset)
		{

#ifdef _UNICODE 

			m_nSnapshotBufferLen = nStringLen;
			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];

			::memset((LPVOID)m_pszSnapshotBuffer, 0, m_nSnapshotBufferLen*sizeof(TCHAR));
			::memcpy((LPVOID)m_pszSnapshotBuffer, lpszString, nStringLen);

#else

			m_nSnapshotBufferLen = nStringLen / 2 + 1;
			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];

			::memset((LPVOID)m_pszSnapshotBuffer, 0, m_nSnapshotBufferLen*sizeof(TCHAR));
			m_nSnapshotStringLen = ::WideCharToMultiByte(CP_ACP, 0, (LPWSTR)lpszString, nStringLen, (LPSTR)m_pszSnapshotBuffer, m_nSnapshotBufferLen, NULL, NULL);

#endif //_UNICODE

		}
		else
		{

#ifdef _UNICODE 

			m_nSnapshotBufferLen = nStringLen;
			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];

			::memset(m_pszSnapshotBuffer, 0, m_nSnapshotBufferLen*sizeof(TCHAR));
			m_nSnapshotStringLen = ::MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lpszString, nStringLen, m_pszSnapshotBuffer, m_nSnapshotBufferLen);
#else

			m_nSnapshotBufferLen = nStringLen;
			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];

			::memset((LPVOID)m_pszSnapshotBuffer, 0, m_nSnapshotBufferLen*sizeof(TCHAR));
			::memcpy((LPVOID)m_pszSnapshotBuffer, lpszString, nStringLen);

#endif //_UNICODE

		}
	}
}
void CHtmlObject::DeleteSnapshot()
{
	//先清除树型表。
	InnerCleanupNode();

	if (m_pszSnapshotBuffer)
		delete[]m_pszSnapshotBuffer;

	m_pszSnapshotBuffer = NULL;
	m_nSnapshotBufferLen = 0;
	m_nSnapshotStringLen = 0;
}
//
void CHtmlObject::Parse()
{
#ifdef _AFX
	CString strTrace;
	strTrace.Format(_T("CHtmlObject::Parse() --begin-->(%d)\r\n"), ::GetTickCount());
	::OutputDebugString(strTrace);
#endif //_AFX

	InnerParse();

#ifdef _AFX
	strTrace.Format(_T("CHtmlObject::Parse() --end-->(%d)\r\n"), ::GetTickCount());
	::OutputDebugString(strTrace);
#endif //_AFX
}
//
void CHtmlObject::InnerParse()
{
	LPTSTR pszFind = m_pszSnapshotBuffer;

	//跳过所有“空格”
	while (*pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind))
	{
		//下一个字符
		pszFind++;
	}
	//直到碰到'\0'就退出
	do
	{
		// 不是“\0”,并且第一个字符为“<”则置换为“\0”,否则什么也不做。
		//这么写的原因就在于InnerSplitContent()返回后 “<”可能已经被置换成“\0”。
		if (*pszFind != _T('\0') && *pszFind == _T('<'))
		{
			//把“<”置换为“\0”,做为结尾。
			*pszFind = _T('\0');
			//下一个字符。
			pszFind++;
		}

		// 不是“\0”
		if (*pszFind != _T('\0'))
		{
			//是否为注释
			if (*pszFind == _T('!'))
			{
				//申请一个点节。
				tagNode *pstNode = InnerAllocNode();
				//解析注释,返回的是注释后面的内容。
				pszFind = InnerSplitComment(pstNode, pszFind);
				//链接到“链表”。(下)
				InnerLinkNextNode(pstNode);
			}
			else
			{
				//申请一个点节。
				tagNode *pstNode = InnerAllocNode();
				//解析tag,返回的是tag后面的内容。
				pszFind = InnerSplitTag(pstNode, pszFind);
				//解析content返回的是content后面的内容。
				pszFind = InnerSplitContent(pstNode, pszFind);
				//链接到“链表”。(下)
				InnerLinkNextNode(pstNode);
			}
		}

	} while (*pszFind != _T('\0'));
}
LPTSTR CHtmlObject::InnerSplitComment(CHtmlObject::tagNode * lpstNode, LPTSTR lpszTagString)
{
	LPTSTR pszFind = lpszTagString;
	//指向注释开头(已经跳过“<”字符)
	lpstNode->s_pszKey = pszFind;
	//如果为 <!-- *** -->
	if (::_tcsnicmp(pszFind + 1, _T("--"), 2) == 0)
	{
		//跳过注释标记“头”,开始查找。
		pszFind += 3;
		//查找到注释结尾,并给结尾加“\0”。
		while (::_tcsnicmp(pszFind, _T("-->"), 3) != 0)
		{
			//下一个字符
			pszFind++;
		}
		//不是“\0”
		if (*pszFind != _T('\0'))
		{
			//把“>”置换为“\0”,做为注释结尾
			*(pszFind + 2) = _T('\0');
			//指向新的节点或内容。
			pszFind += 3;
		}

	}
	//否则为 <! *** >
	else
	{
		//查找到注释结尾,并给结尾加“\0”。
		while (*pszFind != _T('\0') && *pszFind != _T('>'))
		{
			//下一个字符
			pszFind++;
		}
		//不能是“\0”
		if (*pszFind != _T('\0'))
		{
			//把“>”置换为“\0”,做为注释结尾。
			*pszFind = _T('\0');
			//指向新的节点或内容。
			pszFind++;
		}
	}

	//找到一个“<”
	while (*pszFind != _T('\0') && *pszFind != _T('<'))
	{
		//下一个字符
		pszFind++;
	}

	return pszFind;
}
LPTSTR CHtmlObject::InnerSplitTag(CHtmlObject::tagNode * lpstNode, LPTSTR lpszTagString)
{
	LPTSTR pszFind = lpszTagString;

	//指向开头(已经跳过“<”字符)
	lpstNode->s_pszKey = pszFind;
	//查找tag结尾,并给结尾加“\0”。
	while (*pszFind != _T('\0') && *pszFind != _T('>') && !CHtmlObject::IsSpace(*pszFind))
	{
		//下一个字符
		pszFind++;
	}

	//不是“\0”
	if (*pszFind != _T('\0'))
	{
		if (*pszFind == _T('>'))
		{
			//把“>”置换为“\0”,做为注释结尾。
			*pszFind = _T('\0');
			//指向新的节点或内容。
			pszFind++;
			//此tag没有属性,什么也不做了。

		}
		else
		{
			//把“space,\r,\n,\t ”置换为“\0”,做为注释结尾。
			*pszFind = _T('\0');
			//指向新的节点或内容。
			pszFind++;

			//如果不是结束标记,表示此tag有“属性”还需要解析“属性”。
			if (*lpstNode->s_pszKey != _T('/'))
			{
				//跳过所有“空格”,找到第一个属性。
				while (*pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind))
				{
					//下一个字符
					pszFind++;
				}
				//循环分析“属性”。
				while (*pszFind != _T('\0') && *pszFind != _T('<') && *pszFind != _T('>'))
				{
					//例:
					// key="value" key=value
					//跳过空格
					while (*pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind))
					{
						//下一个字符
						pszFind++;
					}

					//不是“\0”
					if (*pszFind != _T('\0'))
					{
						//申请一个点节。
						tagNode *pstAttributeNode = InnerAllocNode();
						//指向“属性”Key。
						pstAttributeNode->s_pszKey = pszFind;

						//查找key的末尾.
						while (*pszFind != _T('\0') && *pszFind != _T('=') && *pszFind != _T('>'))
						{
							//下一个字符
							pszFind++;
						}
						//不是“\0”
						if (*pszFind != _T('\0'))
						{
							if (*pszFind == _T('>'))
							{
								//把“>”置换为“\0”,做为结尾。
								*pszFind = _T('\0');
								//指向新的节点或内容。
								pszFind++;
								//链接到“链表”(右)。
								InnerLinkRightNode(lpstNode, pstAttributeNode);
								//已经碰到“>”,需要跳出。
								break;
							}
							else
							{
								//把“=”置换为“\0”,做为结尾。
								*pszFind = _T('\0');
								//指向新的节点或内容。
								pszFind++;

								//不是“\0”
								if (*pszFind != _T('\0'))
								{
									if (*pszFind == _T('"'))
									{
										//跳过“"”
										pszFind++;
										//指向“属性”key的Value。
										pstAttributeNode->s_pszValue = pszFind;

										//查找Value的末尾.
										while (*pszFind != _T('\0') && *pszFind != _T('\"') && *pszFind != _T('>'))
										{
											//下一个字符
											pszFind++;
										}
										//不是“\0”
										if (*pszFind != _T('\0'))
										{
											//把“",> ”置换为“\0”,做为结尾。
											*pszFind = _T('\0');
											//指向新的节点或内容。
											pszFind++;
										}
									}
									else if (*pszFind == _T('\''))
									{
										//跳过“'”
										pszFind++;
										//指向“属性”key的Value。
										pstAttributeNode->s_pszValue = pszFind;

										//查找Value的末尾.
										while (*pszFind != _T('\0') && *pszFind != _T('\'') && *pszFind != _T('>'))
										{
											//下一个字符
											pszFind++;
										}
										//不是“\0”
										if (*pszFind != _T('\0'))
										{
											//把“",<space> ”置换为“\0”,做为结尾。
											*pszFind = _T('\0');
											//指向新的节点或内容。
											pszFind++;
										}
									}
									else
									{
										//指向“属性”key的Value。
										pstAttributeNode->s_pszValue = pszFind;
										//查找Value的末尾.
										while (*pszFind != _T('\0') && *pszFind != _T(' ') && *pszFind != _T('>'))
										{
											//下一个字符
											pszFind++;
										}
										//不是“\0”
										if (*pszFind != _T('\0'))
										{
											//把“ ”置换为“\0”,做为结尾。
											*pszFind = _T('\0');
											//指向新的节点或内容。
											pszFind++;
										}
									}

								}
							}

						}

						//链接到“链表”(右)。
						InnerLinkRightNode(lpstNode, pstAttributeNode);
					}
				}

				//跳过这个无用的字符。
				if (*pszFind == _T('>'))
				{
					//指向新的节点或内容。
					pszFind++;
				}
			}
		}
	}

	return pszFind;
}
LPTSTR CHtmlObject::InnerSplitContent(CHtmlObject::tagNode * lpstNode, LPTSTR lpszTagString)
{
	LPTSTR pszFind = lpszTagString;

	if (::_tcsnicmp(lpstNode->s_pszKey, _T("script"), 6) == 0)
	{
		pszFind = InnerSplitScript(lpstNode, pszFind);
	}
	else if (::_tcsnicmp(lpstNode->s_pszKey, _T("style"), 5) == 0)
	{
		pszFind = InnerSplitStyle(lpstNode, pszFind);
	}
	else
	{
		pszFind = InnerSplitText(lpstNode, pszFind);
	}

	return pszFind;
}
LPTSTR CHtmlObject::InnerSplitText(CHtmlObject::tagNode * lpstNode, LPTSTR lpszTagString)
{
	LPTSTR pszFind = lpszTagString;

	//跳过所有“空格”
	while (*pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind))
	{
		//下一个字符
		pszFind++;
	}

	//如果 _T('<') 表示没有文本。
	if (*pszFind != _T('<'))
	{
		//指向可见文本。
		lpstNode->s_pszValue = pszFind;
		//查找文本结尾。
		while (*pszFind != _T('\0') && *pszFind != _T('<') && !CHtmlObject::IsSpace(*pszFind))
		{
			//下一个字符
			pszFind++;
		}
		//不是“\0”
		if (*pszFind != _T('\0'))
		{
			if (*pszFind == _T('<'))
			{
				//把“<”置换为“\0”,做为结尾。
				*pszFind = _T('\0');
				//指向新的节点或内容。
				pszFind++;
			}
			else
			{
				//把“space,\r,\n,\t,”置换为“\0”,做为结尾。
				*pszFind = _T('\0');
				//指向新的节点或内容。
				pszFind++;

				//找到一个“<”
				while (*pszFind != _T('\0') && *pszFind != _T('<'))
				{
					//下一个字符
					pszFind++;
				}
			}
		}
	}

	return pszFind;
}
LPTSTR CHtmlObject::InnerSplitScript(tagNode * lpstNode, LPTSTR lpszTagString)
{
	LPTSTR pszFind = lpszTagString;

#define  SCRIPT_MARK_MAX			1024
	UINT nMarkIndex = 0;
	TCHAR szMark[SCRIPT_MARK_MAX] = { _T('\0') }; //max 1024

	//跳过所有“空格”
	while (*pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind))
	{
		//下一个字符
		pszFind++;
	}

	if (*pszFind != _T('\0') && *pszFind != _T('<'))
	{
		//指向可见文本。
		lpstNode->s_pszValue = pszFind;

		while (*pszFind != _T('\0'))
		{
			//如果字符被“',"”包围则为字符串,这期间不计算注释。
			if (szMark[nMarkIndex] != _T('\'') && szMark[nMarkIndex] != _T('\"'))
			{
				//如果是// abc 则跳过。
				if (::_tcsnicmp(pszFind, _T("//"), 2) == 0)
				{
					//跳过注释“头”。
					pszFind += 2;
					//查找注释“尾”。
					while (*pszFind != _T('\0') && *pszFind != _T('\n'))
					{
						pszFind++;
					}
					//跳过注释“尾”。
					if (*pszFind != _T('\0'))
						pszFind++;

				}
				//如果是/* abc */则跳过。
				else if (::_tcsnicmp(pszFind, _T("/*"), 2) == 0)
				{
					//跳过注释“头”。
					pszFind += 2;
					//查找注释“尾”。
					while (::_tcsnicmp(pszFind, _T("*/"), 2) != 0)
					{
						pszFind++;
					}
					//跳过注释“尾”。
					if (*pszFind != _T('\0'))
						pszFind += 2;
				}

			}

			if (*pszFind == _T('\\') &&
				(*(pszFind + 1) == _T('\\') ||
				*(pszFind + 1) == _T('(') || *(pszFind + 1) == _T(')') ||
				*(pszFind + 1) == _T('[') || *(pszFind + 1) == _T(']') ||
				*(pszFind + 1) == _T('{') || *(pszFind + 1) == _T('}') ||
				*(pszFind + 1) == _T('\'') ||
				*(pszFind + 1) == _T('\"')))
			{
				//转意字符
				pszFind += 2;

			}
			else if (*pszFind == _T('{') || *pszFind == _T('(') || *pszFind == _T('[') || (*pszFind == _T('\'') || *pszFind == _T('\"')))
			{
				if (szMark[nMarkIndex] != _T('\'') && szMark[nMarkIndex] != _T('\"'))
				{
					if (nMarkIndex < SCRIPT_MARK_MAX)
					{
						if (nMarkIndex == 0 && szMark[nMarkIndex] == _T('\0'))
							szMark[nMarkIndex] = *pszFind;
						else
							szMark[++nMarkIndex] = *pszFind;
					}
				}
				else if (szMark[nMarkIndex] == *pszFind)
				{
					if (nMarkIndex >0)
						szMark[nMarkIndex--] = _T('\0');
					else
						szMark[nMarkIndex] = _T('\0');
				}
				pszFind++;

			}
			else if (*pszFind == _T('}'))
			{
				if (szMark[nMarkIndex] == _T('{'))
				{
					if (nMarkIndex >0)
						szMark[nMarkIndex--] = _T('\0');
					else
						szMark[nMarkIndex] = _T('\0');
				}
				pszFind++;
			}
			else if (*pszFind == _T(')'))
			{
				if (szMark[nMarkIndex] == _T('('))
				{
					if (nMarkIndex >0)
						szMark[nMarkIndex--] = _T('\0');
					else
						szMark[nMarkIndex] = _T('\0');
				}
				pszFind++;
			}
			else if (*pszFind == _T(']'))
			{
				if (szMark[nMarkIndex] == _T('['))
				{
					if (nMarkIndex >0)
						szMark[nMarkIndex--] = _T('\0');
					else
						szMark[nMarkIndex] = _T('\0');
				}
				pszFind++;
			}
			else if (*pszFind == _T('<') && szMark[0] == _T('\0'))  //nMarkIndex == 0 &&
			{
				//把“<”置换为“\0”,做为结尾。
				*pszFind = _T('\0');
				//指向新的节点或内容。
				pszFind++;
				break;
			}
			else
			{
				pszFind++;
			}
		}
	}

	return pszFind;
}
LPTSTR CHtmlObject::InnerSplitStyle(CHtmlObject::tagNode * lpstNode, LPTSTR lpszTagString)
{
	LPTSTR pszFind = lpszTagString;

#define  STYLE_MARK_MAX			1024
	UINT nMarkIndex = 0;
	TCHAR szMark[STYLE_MARK_MAX] = { _T('\0') }; //max 1024

	//跳过所有“空格”
	while (*pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind))
	{
		//下一个字符
		pszFind++;
	}

	if (*pszFind != _T('\0') && *pszFind != _T('<'))
	{
		//指向可见文本。
		lpstNode->s_pszValue = pszFind;

		while (*pszFind != _T('\0'))
		{
			//如果字符被“(,',"”包围则为字符串,这期间不计算注释。
			if (szMark[nMarkIndex] != _T('(') && szMark[nMarkIndex] != _T('\'') && szMark[nMarkIndex] != _T('\"'))
			{
				//如果是/* abc */则跳过。
				if (::_tcsnicmp(pszFind, _T("/*"), 2) == 0)
				{
					//跳过注释“头”,查找注释“尾”。
					pszFind += 2;
					while (::_tcsnicmp(pszFind, _T("*/"), 2) != 0)
					{
						pszFind++;
					}
					//跳过注释“尾”。
					if (*pszFind != _T('\0'))
						pszFind += 2;
				}
			}

			if (*pszFind == _T('{') || *pszFind == _T('(') || *pszFind == _T('[') || (*pszFind == _T('\'') || *pszFind == _T('\"')))
			{
				if (szMark[nMarkIndex] != _T('\'') && szMark[nMarkIndex] != _T('\"'))
				{
					if (nMarkIndex < STYLE_MARK_MAX)
					{
						if (nMarkIndex == 0 && szMark[nMarkIndex] == _T('\0'))
							szMark[nMarkIndex] = *pszFind;
						else
							szMark[++nMarkIndex] = *pszFind;
					}
				}
				else if (szMark[nMarkIndex] == *pszFind)
				{
					if (nMarkIndex >0)
						szMark[nMarkIndex--] = _T('\0');
					else
						szMark[nMarkIndex] = _T('\0');
				}
				pszFind++;

			}
			else if (*pszFind == _T('}'))
			{
				if (szMark[nMarkIndex] == _T('{'))
				{
					if (nMarkIndex >0)
						szMark[nMarkIndex--] = _T('\0');
					else
						szMark[nMarkIndex] = _T('\0');
				}
				pszFind++;
			}
			else if (*pszFind == _T(')'))
			{
				if (szMark[nMarkIndex] == _T('('))
				{
					if (nMarkIndex >0)
						szMark[nMarkIndex--] = _T('\0');
					else
						szMark[nMarkIndex] = _T('\0');
				}
				pszFind++;
			}
			else if (*pszFind == _T(']'))
			{
				if (szMark[nMarkIndex] == _T('['))
				{
					if (nMarkIndex >0)
						szMark[nMarkIndex--] = _T('\0');
					else
						szMark[nMarkIndex] = _T('\0');
				}
				pszFind++;
			}
			else if (*pszFind == _T('<') && szMark[0] == _T('\0'))  //nMarkIndex == 0 &&
			{
				//把“<”置换为“\0”,做为结尾。
				*pszFind = _T('\0');
				//指向新的节点或内容。
				pszFind++;
				break;
			}
			else
			{
				pszFind++;
			}
		}

	}

	return pszFind;
}

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

#ifndef __JESONYANG_HTMLHELPER_H__
#define __JESONYANG_HTMLHELPER_H__

/*****************************************************************************************************************
created: 2011/12/03
author: JesonYang
blog: http://blog.csdn.net/yc7369
*****************************************************************************************************************/

#include "HtmlObject.h"

class CHtmlHelper :public CHtmlObject
{
public:
	CHtmlHelper(void);
	virtual ~CHtmlHelper(void);
public:
	//
	LPCTSTR GetFirstLink();
	LPCTSTR GetNextLink();
	LPCTSTR GetFirstContent();
	LPCTSTR GetNextContent();
	LPCTSTR	SearchText(LPCTSTR lpszText);
protected:
	//
	CHtmlObject::tagNode * m_pstCur;
};

#endif

//////////////////////////////////////////////////////////////////////////////////////////CHtmlHelper.h//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////CHtmlHelper.cpp//////////////////////////////////////////////////////////////////////////

#pragma once

/*****************************************************************************************************************
created: 2011/12/03
author: JesonYang
blog: http://blog.csdn.net/yc7369
*****************************************************************************************************************/

#include "HtmlHelper.h"
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#pragma warning(disable: 4996) 

CHtmlHelper::CHtmlHelper()
{

}
CHtmlHelper::~CHtmlHelper()
{

}
//
LPCTSTR CHtmlHelper::GetFirstLink()
{
	LPCTSTR pszResult = NULL;

	m_pstCur = m_pstHead;

	while (m_pstCur && !pszResult)
	{
		if (0 != ::_tcsnicmp(m_pstCur->s_pszKey, _T("script"), 6) &&
			0 != ::_tcsnicmp(m_pstCur->s_pszKey, _T("style"), 5))
		{
			CHtmlObject::tagNode * pstAttributeCur = m_pstCur->s_pstRight;
			while (pstAttributeCur)
			{
				if (0 == ::_tcsnicmp(pstAttributeCur->s_pszKey, _T("href"), 4) ||
					0 == ::_tcsnicmp(pstAttributeCur->s_pszKey, _T("src"), 3))
				{
					//return
					pszResult = pstAttributeCur->s_pszValue;
					break;
				}
				else
				{
					pstAttributeCur = pstAttributeCur->s_pstRight;
				}
			}
		}
		m_pstCur = m_pstCur->s_pstNext;
	}

	return pszResult;
}

LPCTSTR CHtmlHelper::GetNextLink()
{
	LPCTSTR pszResult = NULL;

	while (m_pstCur && !pszResult)
	{
		if (0 != ::_tcsnicmp(m_pstCur->s_pszKey, _T("script"), 6) &&
			0 != ::_tcsnicmp(m_pstCur->s_pszKey, _T("style"), 5))
		{
			CHtmlObject::tagNode * pstAttributeCur = m_pstCur->s_pstRight;
			while (pstAttributeCur)
			{
				if (0 == ::_tcsnicmp(pstAttributeCur->s_pszKey, _T("href"), 4) ||
					0 == ::_tcsnicmp(pstAttributeCur->s_pszKey, _T("src"), 3))
				{
					//return
					pszResult = pstAttributeCur->s_pszValue;
					break;
				}
				else
				{
					pstAttributeCur = pstAttributeCur->s_pstRight;
				}
			}
		}

		m_pstCur = m_pstCur->s_pstNext;
	}

	return pszResult;
}

LPCTSTR CHtmlHelper::GetFirstContent()
{
	LPCTSTR pszResult = NULL;

	m_pstCur = m_pstHead;

	while (m_pstCur && !pszResult)
	{
		if (0 != ::_tcsnicmp(m_pstCur->s_pszKey, _T("script"), 6) &&
			0 != ::_tcsnicmp(m_pstCur->s_pszKey, _T("style"), 5))
		{
			if (m_pstCur->s_pszValue)
				pszResult = m_pstCur->s_pszValue;
		}

		m_pstCur = m_pstCur->s_pstNext;
	}

	return pszResult;
}
LPCTSTR CHtmlHelper::GetNextContent()
{
	LPCTSTR pszResult = NULL;

	while (m_pstCur && !pszResult)
	{
		if (0 != ::_tcsnicmp(m_pstCur->s_pszKey, _T("script"), 6) &&
			0 != ::_tcsnicmp(m_pstCur->s_pszKey, _T("style"), 5))
		{
			if (m_pstCur->s_pszValue)
				pszResult = m_pstCur->s_pszValue;
		}

		m_pstCur = m_pstCur->s_pstNext;
	}

	return pszResult;
}
//
LPCTSTR CHtmlHelper::SearchText(LPCTSTR lpszText)
{
	LPCTSTR pszResult = NULL;

	CHtmlObject::tagNode *pstCur = m_pstHead;

	while (pstCur && !pszResult)
	{
		if (0 != ::_tcsnicmp(pstCur->s_pszKey, _T("script"), 6) &&
			0 != ::_tcsnicmp(pstCur->s_pszKey, _T("style"), 5))
		{
			if (pstCur->s_pszValue)
			{
				if ((NULL != ::StrStrI(pstCur->s_pszValue, lpszText)))
					pszResult = pstCur->s_pszValue;
			}
		}

		pstCur = pstCur->s_pstNext;
	}

	return pszResult;
}

#pragma warning(default: 4996) 

//////////////////////////////////////////////////////////////////////////////////////////CHtmlHelper.cpp//////////////////////////////////////////////////////////////////////////
时间: 2024-11-23 12:46:38

C/C++解析 HTML的相关文章

C++工程编译之“error LNK2001: 无法解析的外部符号”

今天一整天都在折腾“error LNK2001: 无法解析的外部符号”,就在头疼不已的时候,总算是找到问题原因了:各个动态链接库的编译方式必须统一才行,要不然很容易对库函数的引用产生冲突.简单来说就是,如果使用的第三方函数库编译方式采用/MD,那么主工程也应该使用/MD.我使用了libevent,而主工程默认采用/MT,所以需要忽略一大堆的函数库,我还纳闷呢,怎么会这么奇怪!!今天总算是解决了长久以来的困惑了. 下面引用一篇文章的描述:[Z]VC运行库版本不同导致链接.LIB静态库时发生重复定义

防止恶意解析——禁止通过IP直接访问网站

一.什么是恶意解析 一般情况下,要使域名能访问到网站需要两步,第一步,将域名解析到网站所在的主机,第二步,在web服务器中将域名与相应的网站绑定.但是,如果通过主机IP能直接访问某网站,那么把域名解析到这个IP也将能访问到该网站,而无需在主机上绑定,也就是说任何人将任何域名解析到这个IP就能访问到这个网站.可能您并不介意通过别人的域名访问到您的网站,但是如果这个域名是未备案域名呢?一旦被查出,封IP.拔线甚至罚款的后果都是需要您来承担的.某些别有用心的人,通过将未备案域名解析到别人的主机上,使其

.NET深入解析LINQ框架(五:IQueryable、IQueryProvider接口详解)

阅读目录: 1.环路执行对象模型.碎片化执行模型(假递归式调用) 2.N层对象执行模型(纵横向对比链式扩展方法) 3.LINQ查询表达式和链式查询方法其实都是空壳子 4.详细的对象结构图(对象的执行原理) 5.IQueryable<T>与IQueryProvider一对一的关系能否改成一对多的关系 6.完整的自定义查询 1]. 环路执行对象模型.碎片化执行模型(假递归式调用) 这个主题扯的可能有点远,但是它关系着整个LINQ框架的设计结构,至少在我还没有搞懂LINQ的本意之前,在我脑海里一直频

.NET深入解析LINQ框架(一:LINQ优雅的前奏)

阅读目录: 1.LINQ简述 2.LINQ优雅前奏的音符 2.1.隐式类型 (由编辑器自动根据表达式推断出对象的最终类型) 2.2.对象初始化器 (简化了对象的创建及初始化的过程) 2.3.Lambda表达式 (对匿名方法的改进,加入了委托签名的类型推断并很好的与表达式树的结合) 2.4.扩展方法 (允许在不修改类型的内部代码的情况下为类型添加独立的行为) 2.5.匿名类型 (由对象初始化器推断得出的类型,该类型在编译后自动创建) 2.6.表达式目录树(用数据结构表示程序逻辑代码) 3.LINQ

.NET深入解析LINQ框架(二:LINQ优雅的前奏)

阅读目录: 1.LINQ框架的主要设计模型 1.1.链式设计模式 (以流水线般的链接方式设计系统逻辑) 1.2.链式查询方法(逐步加工查询表达式中的每一个工作点) 2.LINQ框架的核心设计原理 2.1.托管语言之上的语言(LINQ查询表达式) 2.2.托管语言构造的基础(LINQ依附通用接口与查询操作符对应的方法对接) 2.3.深入IEnumerable.IEnumerable<T>.Enumerable(LINQ to Object框架的入口) 2.4.深入IQueryable.IQuer

.NET深入解析LINQ框架(三:LINQ优雅的前奏)

阅读目录: 1.动态LINQ查询(动态构建Expression<T>表达式树) 2.DLR动态语言运行时(基于CLR之上的动态语言运行时) 1].动态LINQ查询(动态构建Expression<T>表达式树) 什么是动态LINQ查询?LINQ的编写是静态的,因为C#是基于静态类型系统原理设计的,在编写时已经确定类型,也就是在编译时就已经知道将要执行什么样的查询,条件是什么.排序方式是什么等等.那么很大一部分应用场合中我们需要根据用户的选择来查询数据源,以往我们都是通过判断的方式来拼

.NET深入解析LINQ框架(六:LINQ执行表达式)

阅读目录: 1.LINQ执行表达式 在看本篇文章之前我假设您已经具备我之前分析的一些原理知识,因为这章所要讲的内容是建立在之前的一系列知识点之上的,为了保证您的阅读顺利建议您先阅读本人的LINQ系列文章的前几篇或者您已经具备比较深入的LINQ原理知识体系,防止耽误您的宝贵时间. 到目前为止我们对LINQ的执行原理已经很清楚了,从它的前期构想到它真正为我们所用都有足够的证据,但是似乎问题并没有我们想的那么简单,问题总是在我们使用中频频出现尤其是新技术的使用,当然有问题才能有进步. 一:LINQ执行

MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory

从上文<MyBatis框架中Mapper映射配置的使用及原理解析(六) MapperRegistry> 中我们知道DefaultSqlSession的getMapper方法,最后是通过MapperRegistry对象获得Mapper实例: public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory =

Informatica元数据库解析

Informatica全部的元数据信息均以数据库表的方式存到了元数据库中.当然Infa本身工具提供了非常多的人性化的功能.使我们在开发时能够非常方便的进行操作.但人们的需求总是万变的.须要方便的取到自己须要的信息,那就须要我们对他的元数据库有非常深的了解. Informatica通过表和视图给我们提供着全部的信息,在此将通过一个系列的帖子.将大部分常见的,且很实用的表及视图介绍一下.基于这些东西.我们即能够依据不同的需求查出自己须要的数据,也能够开发一些辅助的Infa应用程序. OPB_ATTR

Java并发编程:Concurrent锁机制解析

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd