首页 > 代码库 > C/C++解析 HTML
C/C++解析 HTML
本文在HMM7E的博文http://blog.csdn.net/hmm7e/article/details/7071705 HTML解析-第二版(C/C++)上修正了小部分错误,感谢他给予我的帮助
经修改后的工具类以及使用类大家可直接使用
基于某些不着边际想法,只为取得HTML页面上的所有“URL”和“文本”,其它的内容都不在关心之列。
问题:
对于“文本”搜索,如果搜索了除英文以外的语言还好说些,如果要搜索的内容是英文本,
那么就难以区分是“标记”还是“本文”了。对于“URL”的搜索,因为“标记”就是英文,
这样就绕回到“对于‘文本’搜索”。另外字母的大小写,被转义的字符,引号,尖括号,都得处理。
例如:
<a href=http://www.mamicode.com/"http://www.csdn.net" >csdn
<script src=http://www.mamicode.com/"http://csdnimg.cn/xxxxxxxx.js" type="text/javascript"></script>
想要搜索“csdn”这个字符串,直接以字符串遍历的法能搜索到3个,其实呢只希望搜索到1个。
例如:
<a href=http://www.mamicode.com/"http://bbs.csdn.net" >论坛
<a href=http://www.mamicode.com/"http://bbs.csdn.net" >论 坛
<a href=http://www.mamicode.com/"http://bbs.csdn.net" >论 坛
想要搜索“论坛”这个字符串,按语义上讲,希望在搜索时能搜到3个。
但直接以字符串遍历的法能搜到1个,原因在于加了“空格”后的字符串,
计算机不知道对于人来讲意思并没有变。
总结:
1:直接搜索特定字符串,不多了就是少了。
2:尝试过MS的COM库,功能强大且齐全,但耗费的资源也相当多。
3:耳熟能详的搜索引擎也跑过几个回合,因没有耐心翻遍所有网页只好放弃。
结论:
只能把HTML页面完整的解析完毕才能达找到想到的东西,尽管不是全部,但情况要好很多。
思路:
1.初步分析所有的HTML标签,将其分出必要的层次;
2.层次较高的标签可以对象化,而层次低一些的标签或元素按照用途及使用频率分别对待;
3.凡对象化的标签,在C语言中都可以声明为结构,未对象化的元素可以按性质以相应的数据类型进行存储。
要求:
1.不是所有的标签和元素都可以对象化的,那样会非常复杂;
2.所有被对象化的标签都必须写一套专用的解释和处理程序;
3.上述思路适合数据库的存储。
方法:
HTML语句结构是:<a href=http://www.mamicode.com/"http://www.csdn.net" >aaaa 或 <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 = http://www.mamicode.com/pszFind;>//////////////////////////////////////////////////////////////////////////////////////////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//////////////////////////////////////////////////////////////////////////
C/C++解析 HTML