程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> XML文件解析器TXml,xml解析txml

XML文件解析器TXml,xml解析txml

編輯:C++入門知識

XML文件解析器TXml,xml解析txml


前幾天看了開源的XML文件解析器TinyXml,它是怎麼實現解析的沒怎麼看懂,於是決定自己實現一個,反正最近不忙。先命名為TXml。現在完成了解析和查詢功能,全部代碼加起來不到1000行,將會繼續完善它。源碼必共享

先簡單說一下我的思路:

1:讀取XML文件信息,並存入一個字符數組中;

2:遍歷數組,將數組解析成一棵樹;

3:以路徑的方式查詢和按屬性查詢;

這個解析器最麻煩的地方就在怎麼將字符數組解析成一顆樹。我們先看一下一個簡單XML文件,他包括文件頭、節點、節點名稱及節點值、屬性名稱及屬性值,子節點、父節點、注釋等。

<?xml version="1.0" encoding="utf-8" ?>
<!--注釋-->
<Items>
  <item name="chentaihan">89757</item>
</Items>

簡單介紹一下解析的實現,不太好說清楚,看代碼可能更容易理解一些。遞歸實現,每次都從一個節點開始解析,就是從字符“<”開始,到字符“>”結束,字符<後面就是節點的名稱,之後的就是節點屬性,字符>後一個字符如果不是<,那就是節點的值,如果是字符<,可能是子節點也可能是這個節點結束了。遇到字符<開始遞歸,空格和注釋直接被PASS。大致代碼如下:

復制代碼
const char* TXmlParser::ParseContent(const char* p,XmlNode* baseNode)
{ 
    if(p==NULL || !*p) 
        return NULL; 
    if(*p=='<')//開始一個節點
    {
        bool isNote;
        p=SkipNote(p,isNote);//跳過注釋
        if(isNote) {//是注釋
            ParseContent(p,baseNode); 
            return NULL;
        } 
        if(*p=='/')//結束節點
        {
            while(p!=NULL && *p && *p!='>')
            {
                p++;
            } 
            ++p=SkipWhiteSpace(p);
            ParseContent(p,baseNode->parent);//新節點
        }else{ //節點屬性  
            string name;
            while(p!=NULL && *p && *p!='>' && *p!=' ' && *p!='/')
            {
                 name.push_back(*p++); 
            }
            XmlNode* node=new XmlNode(name,baseNode);
            baseNode->AppendNode(node);

            if(*p=='>')
            {
                ++p=SkipWhiteSpace(p); 
                ParseContent(p,node);//新節點
            }else{
                p=GetAttr(p,node);
                if(*p=='/')
                {
                    while(p!=NULL && *p && *p!='<')
                        p++;
                    ParseContent(p,baseNode);//新節點
                }else{
                    ++p=SkipWhiteSpace(p);  
                    ParseContent(p,node);//新節點
                }
            }  
        }
    }else{//節點的值 
        GetNodeValue(p,baseNode);
    } 
}
復制代碼

按路徑的方式查詢。利用兩個數組實現,假設這兩個數組分別為A,B;第一次查詢將結果存入數組A,將A作為數據源,將查詢結果存入B,清除A中的數據,將B作為數據源,將查詢結果存入A,反復進行,最後A,B中有一個就是查詢結果。當然也可以用遞歸實現,我們都知道遞歸太深容易爆線程棧,且性能低。

按屬性查詢。同樣沒有用遞歸實現,有個經常出現的面試題:按層序打印一個棵樹。那麼這裡也是按層序查找,就是利用一個隊列,按根節點、根節點的直接子節點進棧,一個個匹配,不匹配就出隊列。

復制代碼
//根據屬性查詢--利用隊列按層序查詢
XmlNode* XmlNode::SelectSingleNodeByAttr(const string& attrName,const string& attrValue,XmlNode* node)
{  
    if(node==NULL)
        return NULL; 
    if(node->attribute!=NULL && (*node->attribute)[attrName]==attrValue)
    { 
        return node;
    } 
    queue<XmlNode*> list; 
    for(int i=node->ChildCount()-1;i>=0;i--)
    {
        list.push((*node->childNodes)[i]); 
    }

    while(list.size()>0)
    {
        XmlNode* tmpNode=list.front();
        if(tmpNode->attribute!=NULL && (*tmpNode->attribute)[attrName]==attrValue)
        { 
            return tmpNode;
        }
        for(int i=tmpNode->ChildCount()-1;i>=0;i--)
        {
            list.push((*tmpNode->childNodes)[i]); 
        }
        list.pop();
    } 
    return NULL;
}
復制代碼

看了按屬性查找,我們就很容易知道,C#中ConfigurationManager讀取配置文件的大致實現,因為配置文件很簡單,就是一個節點下面有多個節點,完全可以這樣實現,根節點基本可以無視,直接就是一個字典,KEY存key的值,VALUE存value的值,查找的時間復雜度就是O(1)。

簡單測試:

復制代碼
#include "XmlDocument.h"
 
int main()
{
    {    
        XmlDocument doc;
        doc.Load("test.txt");   
        cout<<"XML頭部:"<<doc.Head().c_str()<<endl;
        cout<<"顯示全部學生成績:" ;
        doc.ShowXML(doc);  
        cout<<endl;
        vector<XmlNode*> vect;
        doc.SelectNodes("students/student/courses/course",vect);
         
        XmlNode* node=doc.SelectSingleNode("students/Student/courses/course/Course"); 

        if(node!=NULL)
        {
            node->ShowXML(*node); 
            cout<<"name:"<<node->Name().c_str()<<endl;
            cout<<"屬性:";
            node->ShowAttr() ; 
            cout<<endl;
            cout<<"value:"<<node->Value().c_str()<<endl;
            cout<<"ChildCount:"<<node->ChildCount()<<endl<<endl;
            
             XmlNode::Iterator iter=node->begin();
            while(iter!=node->end())
            { 
                cout<<"name:"<<(*((iter)._Ptr))->Name().c_str() <<endl;
                cout<<"value:"<<(*((iter)._Ptr))->Value().c_str() <<endl; 
                iter++;
            }  
        }   
        cout<<"查找name=‘英文’的節點:"<<endl;
        XmlNode* node2=doc.SelectSingleNodeByAttr("name","英文");
        if(node2!=NULL){
            node2->ShowXML(*node2); 
        }
    }
    
    system("pause");
    return 0;
}
復制代碼

運行結果如下:

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved