zoukankan      html  css  js  c++  java
  • tinyxml源码解析(中)

    前言:

      之前趁着这段时间比较空闲,也因为听闻tinyxml大名,因此就阅读了tinyxml的源码.在上一篇博文中<tinyxml源码解析(上)>中对tinyxml整体架构以及其中两个基类TiXmlBase,TiXmlNode进行了注释解析.不过文章写得有点啰嗦,太过详细.因此这篇文章将提取其中的部分设计而不是对于每个函数进行注释.

      本文将主要介绍tinyxml对于attribute的处理,各个元素类的简单介绍以及tinyxml的整体处理流程.

    正文:

      还是先回顾下之前看的tinyxml的结构图吧.

      

      从图上我们可以看到,TiXmlAttribute并不是继承与TiXmlNode中,虽然其是xml的元素之一,在实现上很多也是element的叶节点,不过tinyxml并没有这么做.可以先看下TiXmlAttribute的声明.

    View Code
     1 class TiXmlAttribute : public TiXmlBase
     2 {
     3     friend class TiXmlAttributeSet;//TiXMlAttrubteSet之后我们将会看到
     4 public:
     5     //....构造函数
     6 
     7     //返回属性的值
     8     const char*        Name()  const        { return name.c_str(); }        
     9     const char*        Value() const        { return value.c_str(); }        
    10     //....
    11     //提供了比IntValue()更好的错误检查.后头还是调用了sscanf进行处理,而IntValue则是采用了atoi
    12     //QueryXXXValue系列都是采用了sscanf进行处理.
    13     int QueryIntValue( int* _value ) const;
    14     int QueryDoubleValue( double* _value ) const;
    15 
    16     //设置值,对于有类型转化的,采用了sprintf进行处理
    17     void SetName( const char* _name )    { name = _name; }
    18     void SetValue( const char* _value )    { value = _value; }
    19 
    20     void SetIntValue( int _value );                        
    21     void SetDoubleValue( double _value );                
    22 
    23     //提供了string的版本
    24     ///...
    25 
    26     ///简单的比较函数
    27     ///....
    28     ///Parse将在下面统一讲解
    29     virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
    30 
    31 
    32 private:
    33     TiXmlAttribute( const TiXmlAttribute& );                // not implemented.
    34     void operator=( const TiXmlAttribute& base );    // not allowed.
    35 
    36     TiXmlDocument*    document;    // A pointer back to a document, for error reporting.
    37     TIXML_STRING name;
    38     TIXML_STRING value;
    39     TiXmlAttribute*    prev;
    40     TiXmlAttribute*    next;
    41 };

      TiXmlAttribute提供了少部分功能的转化函数.不过特殊的是其Next函数以及Previous函数的实现比较特别.

    View Code
     1 const TiXmlAttribute* TiXmlAttribute::Next() const
     2 {
     3         //这里判断是否到尾部的方式是判断next的成员的值
     4     if ( next->value.empty() && next->name.empty() )
     5         return 0;
     6     return next;
     7 }
     8 
     9 const TiXmlAttribute* TiXmlAttribute::Previous() const
    10 {
    11     // 这里也是
    12     if ( prev->value.empty() && prev->name.empty() )
    13         return 0;
    14     return prev;
    15 }

    原因在于,tinyxml使用了循环双向链表进行处理.看下面的TiXmlAttributeSet就可以看得出了.作者给出的理由是:他喜欢循环链表.另外,说明了相对于典型双向链表的独立性(这个我也没搞明白是怎么回事).

    TiXmlAttributeSet是一个工具类,其负责管理Element中的attribute.提供了添加,删除,查找等功能,使得代码更为简洁.

     1 //折叠起来总是有BUG.打不开.所以就只要不折叠了...
     2 class TiXmlAttributeSet
     3 {
     4 public:
     5     //构造和析构函数,构造函数就是把sentinel的头尾连在一起,而析构则是两句assert,判断是否所有元素被移除.
     6 
     7     void Add( TiXmlAttribute* attribute );
     8     void Remove( TiXmlAttribute* attribute );
     9 
    10     //有元素返回,没元素返回null
    11     const TiXmlAttribute* First()    const    { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
    12     TiXmlAttribute* First()                    { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
    13     const TiXmlAttribute* Last() const        { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
    14     TiXmlAttribute* Last()                    { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
    15 
    16     //查找
    17     TiXmlAttribute*    Find( const char* _name ) const;
    18     TiXmlAttribute* FindOrCreate( const char* _name );
    19     ///stl版本
    20     ///...
    21 
    22 private:
    23     //因为TiXmlAttribute禁止复制,因此AttributeSet也禁止
    24     TiXmlAttributeSet( const TiXmlAttributeSet& );    
    25     void operator=( const TiXmlAttributeSet& );    
    26 
    27     TiXmlAttribute sentinel;//循环链表的头.其目的在于做为头和尾的区分.
    28 };

    可以看看循环链表的处理.

    View Code
     1 void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
     2 {
     3     #ifdef TIXML_USE_STL
     4     assert( !Find( TIXML_STRING( addMe->Name() ) ) );    // Shouldn't be multiply adding to the set.
     5     #else
     6     assert( !Find( addMe->Name() ) );    // Shouldn't be multiply adding to the set.
     7     #endif
     8 
     9     addMe->next = &sentinel;
    10     addMe->prev = sentinel.prev;
    11 
    12     sentinel.prev->next = addMe;
    13     sentinel.prev      = addMe;
    14 }
    15 
    16 void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
    17 {
    18     TiXmlAttribute* node;
    19 
    20     for( node = sentinel.next; node != &sentinel; node = node->next )
    21     {
    22         if ( node == removeMe )
    23         {
    24             node->prev->next = node->next;
    25             node->next->prev = node->prev;
    26             node->next = 0;
    27             node->prev = 0;
    28             return;
    29         }
    30     }
    31     assert( 0 );        // we tried to remove a non-linked attribute.

    在查看了attribute的处理后,我们就可以看看element的处理了.

    View Code
     1 class TiXmlElement : public TiXmlNode
     2 {
     3 public:
     4     
     5     //构造,复制,析构函数
     6     ///...
     7     
     8     ///一系列的Attribute函数仅仅就是调用attributeset中的find,查找到后调用attribute的query函数
     9     ///其他都类似
    10     const char* Attribute( const char* name ) const
    11     {
    12         const TiXmlAttribute* node = attributeSet.Find( name );
    13         if ( node )
    14             return node->Value();
    15         return 0;
    16     }
    17 
    18     //set函数也不例外,就是find然后set
    19     void TiXmlElement::SetAttribute( const char * name, int val )
    20     {    
    21         TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
    22         if ( attrib ) {
    23             attrib->SetIntValue( val );
    24         }
    25     }
    26     
    27     //可想而知,remove肯定也是一样的,调用attributeset的函数进行处理
    28     void RemoveAttribute( const char * name );
    29     
    30     //获取其中第一个文字节点
    31     const char* GetText() const;
    32 
    33     /// 复制当前节点,重写了TiXmlNode的纯虚函数
    34     virtual TiXmlNode* Clone() const;
    35     //打印函数,
    36     virtual void Print( FILE* cfile, int depth ) const;
    37 
    38     //parse函数之后统一说
    39     virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
    40 
    41     //这部分设计的很精巧.在父类TiXmlNode中,所有的ToXXXX函数返回皆为null.而在子类中相应的重写函数,使之转化成为可能.而转化成其他类的函数仍然不可以.
    42     virtual const TiXmlElement*     ToElement()     const { return this; } 
    43     virtual TiXmlElement*           ToElement()              { return this; } 
    44 
    45     virtual bool Accept( TiXmlVisitor* visitor ) const;
    46 
    47 protected:
    48     //复制函数,提供给复制构造函数
    49     void CopyTo( TiXmlElement* target ) const;
    50     void ClearThis();
    51     //获取element中的值.在parse中被调用.
    52     const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
    53 
    54 private:
    55     TiXmlAttributeSet attributeSet;//管理attribute列表
    56 };

    剩下的几个元素,如declaration,unknown,text,comment都和element差别不大.我们直接来看TiXmlDocument:

    View Code
     1 class TiXmlDocument : public TiXmlNode
     2 {
     3 public:
     4     //构造函数
     5     TiXmlDocument()
     6     {
     7         tabsize = 4;//设定输出tab格式
     8         useMicrosoftBOM = false;//设定是否采用ms的utf8-bom格式
     9         ClearError();
    10     }
    11 
    12     //复制构造函数
    13     TiXmlDocument( const TiXmlDocument& copy );
    14     TiXmlDocument& operator=( const TiXmlDocument& copy )
    15     {
    16         Clear();
    17         copy.CopyTo( this );//采用了private的copyTo函数
    18         return *this;
    19     }
    20 
    21     virtual ~TiXmlDocument() {}//析构没什么好做的
    22     
    23     //保存和读取文本函数,
    24     bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
    25     bool SaveFile( FILE* ) const;
    26 
    27     //分析,下面将主要分析这个函数
    28     virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
    29 
    30     //带格式输出
    31     virtual void Print( FILE* cfile, int depth = 0 ) const;
    32     void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
    33 
    34     //同TiXmlElement
    35     virtual const TiXmlDocument*    ToDocument()    const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
    36     virtual TiXmlDocument*          ToDocument()          { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
    37 
    38     /** Walk the XML tree visiting this node and all of its children. 
    39     */
    40     virtual bool Accept( TiXmlVisitor* content ) const;
    41 
    42 protected :
    43     // 重写的Clone函数
    44     virtual TiXmlNode* Clone() const;
    45     #ifdef TIXML_USE_STL
    46     virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
    47     #endif
    48 
    49 private:
    50     //复制的内部函数
    51     void CopyTo( TiXmlDocument* target ) const;
    52 
    53     bool error;
    54     int  errorId;
    55     TIXML_STRING errorDesc;
    56     int tabsize;
    57     TiXmlCursor errorLocation;//定位错误,包涵了行数和列数
    58     bool useMicrosoftBOM;        // 使用utf-bom
    59 };

     大部分只是简单的设置,我们就着重挑几个感兴趣的看看.

    首先是文件操作,LoadFile以及SaveFile

    View Code
     1 //载入数据
     2 bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
     3 {
     4     if ( !file ) 
     5     {
     6         SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
     7         return false;
     8     }
     9 
    10     // 初始化数据
    11     Clear();
    12     location.Clear();
    13 
    14     // 采用fseek的方式来获取文件的大小。并且预分配数据大小,加速大文件处理
    15     long length = 0;
    16     fseek( file, 0, SEEK_END );
    17     length = ftell( file );
    18     fseek( file, 0, SEEK_SET );
    19 
    20     // 比较奇怪的情况。
    21     if ( length <= 0 )
    22     {
    23         SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
    24         return false;
    25     }
    26     
    27     //先new需要读取的数据的大小.
    28     char* buf = new char[ length+1 ];
    29     buf[0] = 0;
    30 
    31     //然后一次性fread进来.
    32     if ( fread( buf, length, 1, file ) != 1 ) {
    33         delete [] buf;
    34         SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
    35         return false;
    36     }
    37 
    38     // 统一回车符,采用p,q两个指针进行处理.
    39     //将\r\n,\n,\r统一转化为\n.
    40     const char* p = buf;    // the read head
    41     char* q = buf;            // the write head
    42     const char CR = 0x0d;
    43     const char LF = 0x0a;
    44 
    45     buf[length] = 0;
    46     while( *p ) {
    47         assert( p < (buf+length) );
    48         assert( q <= (buf+length) );
    49         assert( q <= p );
    50         
    51         //是\r
    52         if ( *p == CR ) {
    53             *q++ = LF;//转化为\n,并且判断下一个字符,,如果是\n,则跳过
    54             p++;
    55             if ( *p == LF ) {        
    56                 p++;
    57             }
    58         }
    59         else {
    60             *q++ = *p++;
    61         }
    62     }
    63     assert( q <= (buf+length) );
    64     *q = 0;
    65 
    66     Parse( buf, 0, encoding );//进行解析,生成xml节点树
    67 
    68     delete [] buf;//删除缓存的数据
    69     return !Error();
    70 }
    71 //save相对简单的多
    72 bool TiXmlDocument::SaveFile( FILE* fp ) const
    73 {
    74     //如果是ms的格式,则写一个UTF8的头
    75     if ( useMicrosoftBOM ) 
    76     {
    77         const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
    78         const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
    79         const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
    80 
    81         fputc( TIXML_UTF_LEAD_0, fp );
    82         fputc( TIXML_UTF_LEAD_1, fp );
    83         fputc( TIXML_UTF_LEAD_2, fp );
    84     }
    85     //然后调用Print进行格式化输出
    86     Print( fp, 0 );
    87     return (ferror(fp) == 0);
    88 }

    接下来是我们用于输出的Print函数:

    View Code
     1 void TiXmlDocument::Print( FILE* cfile, int depth ) const
     2 {
     3     assert( cfile );
     4     for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
     5     {
     6         //使用虚函数继承的方式,调用相应的处理函数
     7         node->Print( cfile, depth );
     8         fprintf( cfile, "\n" );
     9     }
    10 }

    这里用到了多态的方式,来实现相应的函数调用.

    而生成所有xml树的方法Parse也是想同的想法.

     这里就只列举了TiXmlDocument和TiXmlElement的方法.其他的都是类似的处理方式.

    View Code
      1 //在TiXmlDocument::Parse中调用,用于判断接下来数据p中是什么类型的节点,并且生成该类型的节点返回
      2 TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
      3 {
      4     TiXmlNode* returnNode = 0;
      5 
      6     p = SkipWhiteSpace( p, encoding );
      7     //跳过空格后应该是'<',否则要么出错要么结束,返回null
      8     if( !p || !*p || *p != '<' )
      9     {
     10         return 0;
     11     }
     12 
     13     p = SkipWhiteSpace( p, encoding );
     14 
     15     if ( !p || !*p )
     16     {
     17         return 0;
     18     }
     19 
     20     
     21     
     22     const char* xmlHeader = { "<?xml" };
     23     const char* commentHeader = { "<!--" };
     24     const char* dtdHeader = { "<!" };
     25     const char* cdataHeader = { "<![CDATA[" };
     26 
     27     //分别判断接下来是什么
     28     //<?xml 代表是xml文件的declaration
     29     if ( StringEqual( p, xmlHeader, true, encoding ) )
     30     {
     31         #ifdef DEBUG_PARSER
     32             TIXML_LOG( "XML parsing Declaration\n" );
     33         #endif
     34         returnNode = new TiXmlDeclaration();
     35     }
     36     else if ( StringEqual( p, commentHeader, false, encoding ) )
     37     {//<!--代表是注释
     38         #ifdef DEBUG_PARSER
     39             TIXML_LOG( "XML parsing Comment\n" );
     40         #endif
     41         returnNode = new TiXmlComment();
     42     }
     43     else if ( StringEqual( p, cdataHeader, false, encoding ) )
     44     {//<![CDATA代表CDATA的数据,不过xml并没有相应去解析,仅仅做为文字处理
     45         #ifdef DEBUG_PARSER
     46             TIXML_LOG( "XML parsing CDATA\n" );
     47         #endif
     48         TiXmlText* text = new TiXmlText( "" );
     49         text->SetCDATA( true );
     50         returnNode = text;
     51     }
     52     else if ( StringEqual( p, dtdHeader, false, encoding ) )
     53     {//<!代表是dtd,不过由于不支持,所以返回为unknown
     54         #ifdef DEBUG_PARSER
     55             TIXML_LOG( "XML parsing Unknown(1)\n" );
     56         #endif
     57         returnNode = new TiXmlUnknown();
     58     }
     59     else if (    IsAlpha( *(p+1), encoding )
     60               || *(p+1) == '_' )
     61     {//如果是字符,则说明为element
     62         #ifdef DEBUG_PARSER
     63             TIXML_LOG( "XML parsing Element\n" );
     64         #endif
     65         returnNode = new TiXmlElement( "" );
     66     }
     67     else
     68     {//否则就是unknown
     69         #ifdef DEBUG_PARSER
     70             TIXML_LOG( "XML parsing Unknown(2)\n" );
     71         #endif
     72         returnNode = new TiXmlUnknown();
     73     }
     74 
     75     if ( returnNode )
     76     {
     77         // Set the parent, so it can report errors
     78         returnNode->parent = this;
     79     }
     80     return returnNode;
     81 }
     82 
     83 //TiXmlDocument的Parse,在LoadFile时被调用
     84 const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding )
     85 {
     86     ClearError();
     87 
     88     if ( !p || !*p )
     89     {
     90         SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
     91         return 0;
     92     }
     93 
     94     location.Clear();
     95     if ( prevData )
     96     {
     97         location.row = prevData->cursor.row;
     98         location.col = prevData->cursor.col;
     99     }
    100     else
    101     {
    102         location.row = 0;
    103         location.col = 0;
    104     }
    105     //构造parsing data,用于记录数据的位置
    106     TiXmlParsingData data( p, TabSize(), location.row, location.col );
    107     location = data.Cursor();
    108 
    109     if ( encoding == TIXML_ENCODING_UNKNOWN )
    110     {
    111         // 判断是否是utf8 bom格式
    112         const unsigned char* pU = (const unsigned char*)p;
    113         if (    *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
    114              && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
    115              && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )
    116         {
    117             encoding = TIXML_ENCODING_UTF8;
    118             useMicrosoftBOM = true;
    119         }
    120     }
    121     
    122     //跳过所有的空格符,包括\n\r\t ' '等.直接使用english的方式.在utf8情况下也很正常.
    123     p = SkipWhiteSpace( p, encoding );
    124     if ( !p )
    125     {
    126         SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
    127         return 0;
    128     }
    129 
    130     while ( p && *p )
    131     {
    132         //利用Identify的方法来判断是某种元素,并且使用该种元素进行解析接下来的数据.
    133         TiXmlNode* node = Identify( p, encoding );
    134         if ( node )
    135         {
    136             p = node->Parse( p, &data, encoding );
    137             //将节点链接到最后的子节点之后.
    138             //LinkEndChild将节点的所有权直接改到父节点.减少一次复制
    139             LinkEndChild( node );
    140         }
    141         else
    142         {
    143             break;
    144         }
    145 
    146         // Did we get encoding info?
    147         if (    encoding == TIXML_ENCODING_UNKNOWN
    148              && node->ToDeclaration() )
    149         {
    150             TiXmlDeclaration* dec = node->ToDeclaration();
    151             const char* enc = dec->Encoding();
    152             assert( enc );
    153 
    154             if ( *enc == 0 )
    155                 encoding = TIXML_ENCODING_UTF8;
    156             else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
    157                 encoding = TIXML_ENCODING_UTF8;
    158             else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
    159                 encoding = TIXML_ENCODING_UTF8;    // incorrect, but be nice
    160             else 
    161                 encoding = TIXML_ENCODING_LEGACY;
    162         }
    163 
    164         p = SkipWhiteSpace( p, encoding );
    165     }
    166 
    167     // Was this empty?
    168     if ( !firstChild ) {
    169         SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );
    170         return 0;
    171     }
    172 
    173     return p;
    174 }
    175 
    176 //TiXmlElement::Parse,在TiXmlDocument中,检测到是element节点后被调用
    177 //p代表读取的文本数据
    178 const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
    179 {
    180     p = SkipWhiteSpace( p, encoding );
    181     TiXmlDocument* document = GetDocument();
    182 
    183     //读取element时不应该出现结束的情况,返回错误
    184     if ( !p || !*p )
    185     {
    186         if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
    187         return 0;
    188     }
    189 
    190     if ( data )
    191     {
    192         //计算目前处理的字符所在的位置
    193         //会有一定的性能影响.可以选择关闭
    194         data->Stamp( p, encoding );
    195         location = data->Cursor();
    196     }
    197 
    198     //这个应该不会遇到
    199     if ( *p != '<' )
    200     {
    201         if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
    202         return 0;
    203     }
    204 
    205     p = SkipWhiteSpace( p+1, encoding );
    206 
    207     //获取节点的名字
    208     const char* pErr = p;
    209 
    210     //TiXMlElement的工具函数,读取tag的name
    211     p = ReadName( p, &value, encoding );
    212     if ( !p || !*p )
    213     {
    214         if ( document )    document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
    215         return 0;
    216     }
    217 
    218     //构造结尾标识符
    219     TIXML_STRING endTag ("</");
    220     endTag += value;
    221 
    222     // 获取element中的属性,结尾标识符
    223     while ( p && *p )
    224     {
    225         pErr = p;
    226         p = SkipWhiteSpace( p, encoding );
    227         if ( !p || !*p )
    228         {
    229             if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
    230             return 0;
    231         }
    232         if ( *p == '/' )
    233         {
    234             ++p;
    235             // 是空的tag,不具有子节点, <xxx   />
    236             if ( *p  != '>' )
    237             {
    238                 if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );        
    239                 return 0;
    240             }
    241             return (p+1);
    242         }
    243         else if ( *p == '>' )
    244         {
    245             // 处理完属性,下一步处理子节点
    246             ++p;
    247             //ReadValue会处理<x> </x>之间内的所有数据,包括文字,子节点.处理结束后返回
    248             p = ReadValue( p, data, encoding );        
    249             if ( !p || !*p ) {
    250                 //到结尾了,这不应该发生
    251                 if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
    252                 return 0;
    253             }
    254 
    255             if ( StringEqual( p, endTag.c_str(), false, encoding ) )
    256             {
    257                 p += endTag.length();
    258                 p = SkipWhiteSpace( p, encoding );
    259                 if ( p && *p && *p == '>' ) {
    260                     ++p;
    261                     return p;
    262                 }
    263                 if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
    264                 return 0;
    265             }
    266             else
    267             {
    268                 if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
    269                 return 0;
    270             }
    271         }
    272         else
    273         {
    274             // 是属性,进行处理
    275             //生成一个属性节点,然后调用该节点parse接下来的数据
    276             TiXmlAttribute* attrib = new TiXmlAttribute();
    277             if ( !attrib )
    278             {
    279                 return 0;
    280             }
    281 
    282             attrib->SetDocument( document );
    283             pErr = p;
    284             p = attrib->Parse( p, data, encoding );
    285 
    286             if ( !p || !*p )
    287             {
    288                 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
    289                 delete attrib;
    290                 return 0;
    291             }
    292 
    293             // 重复属性,直接报错,返回
    294             #ifdef TIXML_USE_STL
    295             TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );
    296             #else
    297             TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
    298             #endif
    299             if ( node )
    300             {
    301                 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
    302                 delete attrib;
    303                 return 0;
    304             }
    305 
    306             //将该节点加入到attributeset中.
    307             attributeSet.Add( attrib );
    308         }
    309     }
    310     return p;
    311 }

    后语:

      关于tinyxml的介绍大致到这里了.本文介绍了tinyxml的整体处理流程以及其实现的一些细节.之前也有读者提示说tinyxml很慢.而慢的原因有很多,一个是其用了很多多态,而多态是有一定的代价的.另外,其整体实现都是以树的方式完成的,包括element的属性是以链表的方式实现的,查找等效率都比较低下.

  • 相关阅读:
    Linux和Windows下查看环境变量方法对比zz
    建议在虚拟机下使用网银,
    visualfirebug
    JavaScript Guide
    JavaScript Guide
    virtualbox for windows的版本可以好好读一读
    Crossfire
    How To: Convert VirtualPC .vhd hard drives to VirtualBox .vdi hard drives (or viceversa)
    中文网页重设与排版:TYPO.CSS
    AppFuse QuickStart
  • 原文地址:https://www.cnblogs.com/marchtea/p/2766756.html
Copyright © 2011-2022 走看看