首页 > 代码库 > DuiLib 源码分析之解析xml类CMarkup & CMarkupNode cpp文件

DuiLib 源码分析之解析xml类CMarkup & CMarkupNode cpp文件

时隔5个月才有时间接着写未完成的实现部分,也是惭愧呀

选几个关机的函数来解析,一些get方法就忽略掉吧

CMarkupNode 与 CMarkUp 互为友元类,CMarkUp 实现解析,CMarkupNode 用于存储读取节点数据

 1 void CMarkupNode::_MapAttributes() 2 { 3     m_nAttributes = 0; 4     LPCTSTR pstr = m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iStart; 5     LPCTSTR pstrEnd = m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iData; 6     pstr += _tcslen(pstr) + 1; 7     while( pstr < pstrEnd ) { 8         m_pOwner->_SkipWhitespace(pstr); 9         m_aAttributes[m_nAttributes].iName = pstr - m_pOwner->m_pstrXML;//位移10         pstr += _tcslen(pstr) + 1;11         m_pOwner->_SkipWhitespace(pstr);12         if( *pstr++ != _T(\") ) return; // if( *pstr != _T(‘\"‘) ) { pstr = ::CharNext(pstr); return; }13         14         m_aAttributes[m_nAttributes++].iValue = http://www.mamicode.com/pstr - m_pOwner->m_pstrXML;//位移15         if( m_nAttributes >= MAX_XML_ATTRIBUTES ) return;16         pstr += _tcslen(pstr) + 1;17     }18 }

这个函数的主要作用是将已经处理过的xml文件进行数据分割保存,这里保存的属性名和属性值都是xml在内存中的位移,最大属性支持64个

接下来详细说明CMarkUp类

有几个用于加载xml文件的函数:

bool CMarkup::Load(LPCTSTR pstrXML)//直接解析字符串

bool CMarkup::LoadFromMem(BYTE* pByte, DWORD dwSize, int encoding)//将二进制数据流转换为字符串再解析

bool CMarkup::LoadFromFile(LPCTSTR pstrFilename, int encoding)//解析xml文件,根据文件名解析,先判断资源是否被打包到zip压缩包中

1 bool CMarkup::_Parse()//解析入口, 先拓展节点保证有足够的节点存储,然后解析2 {3     _ReserveElement(); // Reserve index 0 for errors4     ::ZeroMemory(m_szErrorMsg, sizeof(m_szErrorMsg));5     ::ZeroMemory(m_szErrorXML, sizeof(m_szErrorXML));6     LPTSTR pstrXML = m_pstrXML;7     return _Parse(pstrXML, 0);8 }
1 CMarkup::XMLELEMENT* CMarkup::_ReserveElement()//拓展节点数2 {3     if( m_nElements == 0 ) m_nReservedElements = 0;4     if( m_nElements >= m_nReservedElements ) {5         m_nReservedElements += (m_nReservedElements / 2) + 500;6         m_pElements = static_cast<XMLELEMENT*>(realloc(m_pElements, m_nReservedElements * sizeof(XMLELEMENT)));//这里的realloc函数会将原来的内容复制到新申请的内存中7     }8     return &m_pElements[m_nElements++];9 }
 1 bool CMarkup::_Parse(LPTSTR& pstrText, ULONG iParent) 2 { 3     _SkipWhitespace(pstrText);//跳过空格 4     ULONG iPrevious = 0; 5     for( ; ; )  6     { 7         if( *pstrText == _T(\0) && iParent <= 1 ) return true;//退出条件,到结尾,或者无父节点 8         _SkipWhitespace(pstrText); 9         if( *pstrText != _T(<) ) return _Failed(_T("Expected start tag"), pstrText);10         if( pstrText[1] == _T(/) ) return true;11         *pstrText++ = _T(\0);12         _SkipWhitespace(pstrText);13         // Skip comment or processing directive 跳过注释(<- ->)或指令(<? ?>)14         if( *pstrText == _T(!) || *pstrText == _T(?) ) {15             TCHAR ch = *pstrText;16             if( *pstrText == _T(!) ) ch = _T(-);17             while( *pstrText != _T(\0) && !(*pstrText == ch && *(pstrText + 1) == _T(>)) ) pstrText = ::CharNext(pstrText);18             if( *pstrText != _T(\0) ) pstrText += 2;19             _SkipWhitespace(pstrText);20             continue;21         }22         _SkipWhitespace(pstrText);23         // Fill out element structure24         XMLELEMENT* pEl = _ReserveElement();25         ULONG iPos = pEl - m_pElements;26         pEl->iStart = pstrText - m_pstrXML;27         pEl->iParent = iParent;28         pEl->iNext = pEl->iChild = 0;29         if( iPrevious != 0 ) m_pElements[iPrevious].iNext = iPos;30         else if( iParent > 0 ) m_pElements[iParent].iChild = iPos;31         iPrevious = iPos;32         // Parse name33         LPCTSTR pstrName = pstrText;34         _SkipIdentifier(pstrText);35         LPTSTR pstrNameEnd = pstrText;36         if( *pstrText == _T(\0) ) return _Failed(_T("Error parsing element name"), pstrText);37         // Parse attributes38         if( !_ParseAttributes(pstrText) ) return false;            //解析属性39         _SkipWhitespace(pstrText);40         if( pstrText[0] == _T(/) && pstrText[1] == _T(>) )    //结尾是/>情况41         {42             pEl->iData = http://www.mamicode.com/pstrText - m_pstrXML;                    //保存节点的结尾位移43             *pstrText = _T(\0);44             pstrText += 2;45         }    46         else                                                    //结尾是>情况47         {48             if( *pstrText != _T(>) ) return _Failed(_T("Expected start-tag closing"), pstrText);49             // Parse node data50             pEl->iData = http://www.mamicode.com/++pstrText - m_pstrXML;51             LPTSTR pstrDest = pstrText;52             if( !_ParseData(pstrText, pstrDest, _T(<)) ) return false;//找到<符号53             // Determine type of next element54             if( *pstrText == _T(\0) && iParent <= 1 ) return true;    //如果是结尾则返回55             if( *pstrText != _T(<) ) return _Failed(_T("Expected end-tag start"), pstrText);56             if( pstrText[0] == _T(<) && pstrText[1] != _T(/) ) 57             {58                 if( !_Parse(pstrText, iPos) ) return false;                //递归解析子节点59             }60             if( pstrText[0] == _T(<) && pstrText[1] == _T(/) )        //处理</>情况61             {62                 *pstrDest = _T(\0);63                 *pstrText = _T(\0);64                 pstrText += 2;65                 _SkipWhitespace(pstrText);66                 SIZE_T cchName = pstrNameEnd - pstrName;67                 if( _tcsncmp(pstrText, pstrName, cchName) != 0 ) return _Failed(_T("Unmatched closing tag"), pstrText);68                 pstrText += cchName;69                 _SkipWhitespace(pstrText);70                 if( *pstrText++ != _T(>) ) return _Failed(_T("Unmatched closing tag"), pstrText);71             }72         }73         *pstrNameEnd = _T(\0);74         _SkipWhitespace(pstrText);75     }76 }
  1 void CMarkup::_SkipWhitespace(LPCTSTR& pstr) const  2 {  3     while( *pstr > _T(\0) && *pstr <= _T( ) ) pstr = ::CharNext(pstr);  4 }  5   6 void CMarkup::_SkipWhitespace(LPTSTR& pstr) const  7 {  8     while( *pstr > _T(\0) && *pstr <= _T( ) ) pstr = ::CharNext(pstr);  9 } 10  11 void CMarkup::_SkipIdentifier(LPCTSTR& pstr) const 12 { 13     // 属性只能用英文,所以这样处理没有问题 14     while( *pstr != _T(\0) && (*pstr == _T(_) || *pstr == _T(:) || _istalnum(*pstr)) ) pstr = ::CharNext(pstr); 15 } 16  17 void CMarkup::_SkipIdentifier(LPTSTR& pstr) const 18 { 19     // 属性只能用英文,所以这样处理没有问题 20     while( *pstr != _T(\0) && (*pstr == _T(_) || *pstr == _T(:) || _istalnum(*pstr)) ) pstr = ::CharNext(pstr); 21 } 22  23 bool CMarkup::_ParseAttributes(LPTSTR& pstrText) 24 {    25     if( *pstrText == _T(>) ) return true; 26     *pstrText++ = _T(\0); 27     _SkipWhitespace(pstrText); 28     while( *pstrText != _T(\0) && *pstrText != _T(>) && *pstrText != _T(/) ) { 29         _SkipIdentifier(pstrText);            //跳过属性名 30         LPTSTR pstrIdentifierEnd = pstrText; 31         _SkipWhitespace(pstrText);            //跳过空白 32         if( *pstrText != _T(=) ) return _Failed(_T("Error while parsing attributes"), pstrText); 33         *pstrText++ = _T( );                //‘=‘也赋值为空格 34         *pstrIdentifierEnd = _T(\0); 35         _SkipWhitespace(pstrText); 36         if( *pstrText++ != _T(\") ) return _Failed(_T("Expected attribute value"), pstrText);//必须为双引号 37         LPTSTR pstrDest = pstrText; 38         if( !_ParseData(pstrText, pstrDest, _T(\")) ) return false;//解析属性数据 39         if( *pstrText == _T(\0) ) return _Failed(_T("Error while parsing attribute string"), pstrText); 40         *pstrDest = _T(\0); 41         if( pstrText != pstrDest ) *pstrText = _T( ); 42         pstrText++; 43         _SkipWhitespace(pstrText); 44     } 45     return true; 46 } 47  48 bool CMarkup::_ParseData(LPTSTR& pstrText, LPTSTR& pstrDest, char cEnd) 49 { 50     while( *pstrText != _T(\0) && *pstrText != cEnd ) { 51         if( *pstrText == _T(&) ) { 52             while( *pstrText == _T(&) ) { 53                 _ParseMetaChar(++pstrText, pstrDest);//解析同义字符&quot;等 54             } 55             if (*pstrText == cEnd) 56                 break; 57         } 58  59         if( *pstrText == _T( ) ) { 60             *pstrDest++ = *pstrText++; 61             if( !m_bPreserveWhitespace ) _SkipWhitespace(pstrText); 62         } 63         else { 64             LPTSTR pstrTemp = ::CharNext(pstrText); 65             while( pstrText < pstrTemp) { 66                 *pstrDest++ = *pstrText++; 67             } 68         } 69     } 70     // Make sure that MapAttributes() works correctly when it parses 71     // over a value that has been transformed. 72     LPTSTR pstrFill = pstrDest + 1; 73     while( pstrFill < pstrText ) *pstrFill++ = _T( );//填充空格,比如存在&quot;情况 74     return true; 75 } 76  77 void CMarkup::_ParseMetaChar(LPTSTR& pstrText, LPTSTR& pstrDest) 78 { 79     if( pstrText[0] == _T(a) && pstrText[1] == _T(m) && pstrText[2] == _T(p) && pstrText[3] == _T(;) ) { 80         *pstrDest++ = _T(&); 81         pstrText += 4; 82     } 83     else if( pstrText[0] == _T(l) && pstrText[1] == _T(t) && pstrText[2] == _T(;) ) { 84         *pstrDest++ = _T(<); 85         pstrText += 3; 86     } 87     else if( pstrText[0] == _T(g) && pstrText[1] == _T(t) && pstrText[2] == _T(;) ) { 88         *pstrDest++ = _T(>); 89         pstrText += 3; 90     } 91     else if( pstrText[0] == _T(q) && pstrText[1] == _T(u) && pstrText[2] == _T(o) && pstrText[3] == _T(t) && pstrText[4] == _T(;) ) { 92         *pstrDest++ = _T(\"); 93         pstrText += 5; 94     } 95     else if( pstrText[0] == _T(a) && pstrText[1] == _T(p) && pstrText[2] == _T(o) && pstrText[3] == _T(s) && pstrText[4] == _T(;) ) { 96         *pstrDest++ = _T(\‘); 97         pstrText += 5; 98     } 99     else {100         *pstrDest++ = _T(&);101     }102 }

解析xml的基本原理就是,将xml加载到内存中,顺序解析节点,首先对节点进行存储,对xml进行改写(将<、>、/、"、‘等改写为空格),获取节点

属性的时候进行分割存储。

 

简单举个例子会更清晰:

 1 <?xml version="1.0" encoding="utf-8"?> 2 <Window size="800,572" sizebox="4,4,6,6" roundcorner="5,5" caption="0,0,0,90" mininfo="800,570"> 3   <Font name="宋体" size="13" bold="true" /> 4  <VerticalLayout bkcolor="#FFD1E8F5" bkcolor2="#FFC6E0F1" bordercolor="#FF768D9B" bordersize="1" borderround="5,5" inset="1,0,1,0"> 5       <HorizontalLayout> 6         <Container width="22" height="22" bkimage="file=‘icon.png‘ source=‘0,0,16,16‘ dest=‘5,4,21,20‘ " /> 7         <Text text="360安全卫士7.3" pos="22, 5, 200, 24" float="true" textcolor="#FF447AA1" font="0" /> 8       </HorizontalLayout>  9  </VerticalLayout>  10  </Window>

比如解析上述xml文件

 \0Window\0size\0 800,572\0 sizebox\0 4,4,6,6\0 roundcorner\0 \05,5\0 caption\0 0,0,0,90\0 mininfo\0 800,570\0>  \0Font\0name\0 宋体\0 size\0 13\0  bold\0 true\0 \0> \0VerticalLayout\0bkcolor\0 #FFD1E8F5\0 bkcolor2\0 #FFC6E0F1\0 bordercolor\0 #FF768D9B\0 bordersize\0 1\0 borderround\0 5,5\0 inset\0 1,0,1,0\0>      \0HorizontalLayout\0>        \0Container\0 width\0 22\0 height\0 22\0 bkimage\0 file\0‘ icon.png‘ source=‘0,0,16,16‘ dest=‘5,4,21,20‘ \0 \0>        \0Text\0 text\0 360安全卫士7.3\0 pos\0 22, 5, 200, 24\0 float\0 true\0 textcolor\0 #FF447AA1\0 font\0 0\0 \0>      \0\0HorizontalLayout>  \0\0VerticalLayout>   \0\0Window>
 1 void CMarkupNode::_MapAttributes() 2 { 3     m_nAttributes = 0; 4     LPCTSTR pstr = m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iStart; 5     LPCTSTR pstrEnd = m_pOwner->m_pstrXML + m_pOwner->m_pElements[m_iPos].iData; 6     pstr += _tcslen(pstr) + 1; 7     while( pstr < pstrEnd ) { 8         m_pOwner->_SkipWhitespace(pstr); 9         m_aAttributes[m_nAttributes].iName = pstr - m_pOwner->m_pstrXML;//位移10         pstr += _tcslen(pstr) + 1;11         m_pOwner->_SkipWhitespace(pstr);12         if( *pstr++ != _T(\") ) return; // if( *pstr != _T(‘\"‘) ) { pstr = ::CharNext(pstr); return; }13         14         m_aAttributes[m_nAttributes++].iValue = http://www.mamicode.com/pstr - m_pOwner->m_pstrXML;//位移15         if( m_nAttributes >= MAX_XML_ATTRIBUTES ) return;16         pstr += _tcslen(pstr) + 1;17     }18 }

然后看获取属性的函数就一目了然了

DuiLib 源码分析之解析xml类CMarkup & CMarkupNode cpp文件