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 = 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 errors
4     ::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 structure
24         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 name
33         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 attributes
38         if( !_ParseAttributes(pstrText) ) return false;            //解析属性
39         _SkipWhitespace(pstrText);
40         if( pstrText[0] == _T(‘/‘) && pstrText[1] == _T(‘>‘) )    //结尾是/>情况
41         {
42             pEl->iData = 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 data
50             pEl->iData = ++pstrText - m_pstrXML;
51             LPTSTR pstrDest = pstrText;
52             if( !_ParseData(pstrText, pstrDest, _T(‘<‘)) ) return false;//找到<符号
53             // Determine type of next element
54             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 = pstr - m_pOwner->m_pstrXML;//位移
15         if( m_nAttributes >= MAX_XML_ATTRIBUTES ) return;
16         pstr += _tcslen(pstr) + 1;
17     }
18 }

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

时间: 2024-08-03 18:06:17

DuiLib 源码分析之解析xml类CMarkup & CMarkupNode cpp文件的相关文章

DuiLib 源码分析之解析xml类CMarkup &amp; CMarkupNode 头文件

xml使用的还是比较多的,duilib界面也是通过xml配置实现的 duilib提供了CMarkkup和CMarkupNode类解析xml,使用起来也是比较方便的,比较好奇它是怎么实现的,如果自己来写一个 解析又需要怎样架构,架构之路还很遥远... 先来看看头文件吧,CMarkup主要是用于分割xml,判断xml格式是否正确:CMarkupNode主要是将CMarkup分割的xml,获取节点中的属性, 最多支持64个属性 1 enum 2 { 3 XMLFILE_ENCODING_UTF8 =

Duilib源码分析(一)整体框架

Duilib界面库是一款由杭州月牙儿网络技术有限公司开发的界面开源库,以viksoe项目下的UiLib库的基础上开发(此后也将对UiLib库进行源码分析):通过XML布局界面,将用户界面和处理逻辑彻底分离,极大地提高用户界面的开发效率.一般常用于开发小型项目Windows桌面客户端软件:其子窗口不以窗口句柄的形式创建,只是逻辑上的窗口,绘制在父窗口之上.目前开源协议以BSD发布,可使用于商业应用,好了,其他更为详细的介绍,请查阅其官网或百度. 源码获取: 目前duilib不在被维护,基本上网络中

Python:Sqlmap源码精读之解析xml

XML <?xml version="1.0" encoding="UTF-8"?> <root> <!-- MySQL --> <dbms value="MySQL"> <cast query="CAST(%s AS CHAR)"/> <length query="LENGTH(%s)"/> <isnull query=&quo

MyBatis 源码分析 - 配置文件解析过程

* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAliases和typeHandlers等,本文的篇幅也主要在对这三个配置解析过程的分析上.下面,我们来一起看一下本篇文章的目录结构. 从目录上可以看出,2.3节.2.5节和2.8节的内容比较多.其中2.3节是关于settings配置解析过程的分析,除了对常规的 XML 解析过程分析,本节额外的分析了元

Scrapy源码分析-常用的爬虫类-CrawlSpider(三)

CrawlSpider classscrapy.contrib.spiders.CrawlSpider 爬取一般网站常用的spider.其定义了一些规则(rule)来提供跟进link的方便的机制. 也许该spider并不是完全适合您的特定网站或项目,但其对很多情况都使用. 因此您可以以其为起点,根据需求修改部分方法.当然您也可以实现自己的spider.除了从Spider继承过来的(您必须提供的)属性外,其提供了一个新的属性: rules: Rule对象集合.定义了提取需要跟进url的一些规则.

Spring Developer Tools 源码分析:二、类路径监控

在 Spring Developer Tools 源码分析一中介绍了 devtools 提供的文件监控实现,在第二部分中,我们将会使用第一部分提供的目录监控功能,实现对开发环境中 classpath 的监控. 二.类路径监控 首先看一些这一部分可能涉及到的类图: 在图中,红色斜线左上部分是第一部分中介绍的文件目录监控的类,其中 FileSystemWatcher 会通过独立线程监控指定的目录,当目录内容发生变化时,通过对比快照可以获得所有监控目录变化的文件ChangedFiles,然后将变化通知

Duilib源码分析(三)XML解析器—CMarkup

上一节介绍了控件构造器CDialogBuilder,接下来将分析其XML解析器CMarkup: CMarkup:xml解析器,目前内置支持三种编码格式:UTF8.UNICODE.ASNI:CMarkupNode:xml节点类 先介绍CMarkup: XMLELEMENT: xml节点元素类型定义,iStart,节点元素在xml文件中的起始位置:iChild,节点元素子节点:iNext,节点元素的下一个节点(兄弟节点):iParent,节点元素的父节点:iData, 节点元素的数据. CMarku

数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(二)之SQL解析

关注微信公众号:[芋艿的后端小屋]有福利: RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址 您对于源码的疑问每条留言都将得到认真回复.甚至不知道如何读源码也可以请教噢. 新的源码解析文章实时收到通知.每周更新一篇左右. 认真的源码交流微信群. 1. 概述 2. SQLParsingEngine 3. SQLParser SQL解析器 3.2.1 #parse

数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(一)之语法解析

关注微信公众号:[芋艿的后端小屋]有福利: RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址 您对于源码的疑问每条留言都将得到认真回复.甚至不知道如何读源码也可以请教噢. 新的源码解析文章实时收到通知.每周更新一篇左右. 1. 概述 2. Lexer 词法解析器 3. Token 词法标记 3.2.1 Literals.IDENTIFIER 词法关键词 3.2