1. 程式人生 > >Win系統C++ TinyXML使用心得總結

Win系統C++ TinyXML使用心得總結

一、TinyXml的特點

        TinyXml是一個基於DOM模型的、非驗證的輕量級C 直譯器。它是一個開源的解析XML的解析庫,能夠用於C++,能夠在Windows或Linux中編譯。這個解析庫的模型通過解析XML檔案,然後在記憶體中生成DOM模型,從而讓我們很方便的遍歷這棵XML樹。

1. SAX和DOM

        目前XML的解析主要有兩大模型:SAX和DOM。
        其中SAX是基於事件的,其基本工作流程是分析XML文件,當發現了一個新的元素時,產生一個對應事件,並呼叫相應的使用者處理函式。這種方式佔用記憶體少,速度快,但使用者程式相應得會比較複雜。
        而DOM(文件物件模型),則是在分析時,一次性的將整個XML文件進行分析,並在記憶體中形成對應的樹結構,同時,向用戶提供一系列的介面來訪問和編輯該樹結構。這種方式佔用記憶體大,速度往往慢於SAX,但可以給使用者提供一個面向物件的訪問介面,對使用者更為友好。

2. 驗證和非驗證

       對 於一個特定的XML文件而言,其正確性分為兩個層次。首先是其格式應該符合XML的基本格式要求,比如第一行要有宣告,標籤的巢狀層次必須前後一致等等, 符合這些要求的檔案,就是一個合格的XML檔案,稱作well-formatted。但除此之外,一個XML文件因其內容的不同還必須在語義上符合相應的 標準,這些標準由相應的DTD檔案或者Schema檔案來定義,符合了這些定義要求的XML檔案,稱作valid。
       因此,解析器也分為兩種,一種是驗證的,即會跟據XML檔案中的宣告,用相應的DTD檔案對XML檔案進行校驗,檢查它是否滿足DTD檔案的要求。另一種是忽略DTD檔案,只要基本格式正確,就可以進行解析。
        就我所知,驗證的解析器通常都是比較重量級的。TinyXml不支援驗證,但是體積很小,用在解析格式較為簡單的XML檔案,比如配置檔案時,特別的合適。

二、構建與使用

1. 獲取

      TinyXml從這裡可以找到最新版本的原始碼,目前的版本是 2.6.2 (截至2011.11.01)。好久沒有更新了。

      在網上還有一個類同的TinyXml-2版本,github開原始碼,其有線上html版本可以參閱。

2.構建

        TinyXml 在構建時可以選擇是否支援STL,選擇的話,則可以使用std::string,所以通常應在Windows上,TinyXml的原始碼包裡提供了VC6的 工程檔案,直接用它就可以生成兩個靜該開啟這個選項。態庫(帶STL和不帶STL),非常容易。唯一需要注意的是,預設生成的庫是單執行緒的,如果用在多線 程的專案中,需要改動一下配置,生成相應的多執行緒庫。

3. 使用

        構建了相應的庫之後,在 使用了它們的工程中,只要在連線時把他們連上就行了。需要注意的是,如果需要STL支援,在編譯用到了TinyXml的檔案時,需要定義一個巨集 TIXML_USE_STL,對gcc,可以使用引數-DTIXML_USE_STL,對cl.exe(VC),可以使用引數 /DTIXML_USE_STL,如果嫌麻煩,可以直接定義在 tinyxml.h檔案裡。

        TinyXml它由兩個標頭檔案(.h檔案)和四個CPP檔案(.cpp檔案)構成,用的時候,只要將(tinyxml.h、tinystr.h、tinystr.cpp、tinyxml.cpp、tinyxmlerror.cpp、tinyxmlparser.cpp)匯入工程就可以用它的東西了。如果需要,可以將它做成自己的DLL來呼叫。

簡單示例:

#include "tinyxml.h"
#include "tinystr.h"
#include <iostream>

using namespace std;
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        cout << "usage: "<<argv[0] << " xmlfile" << endl;
        return 1;
    }
    TiXmlDocument doc(argv[1]);
    bool loadOk = doc.LoadFile();
    if (!loadOk)
    {
        cout << "could load:" << doc.ErrorDesc() << endl;
    }
    TiXmlPrinter printer;//提供的工具類,目的是將xml的資料按格式輸出
    doc.Accept(&printer);
    cout  << printer.CStr() << endl;//輸出

    TiXmlElement*node = doc.FirstChildElement();//獲取第一個element節點
    cout << node->Value() << endl;//輸出節點的值
    string t;
    node->QueryValueAttribute("type", &t);//獲取節點屬性
    cout << t << endl;

    doc.FirstChild()->NextSibling()->ToElement()->QueryStringAttribute("type", &t);//獲取第二個子節點的資料
    cout << "2:" << t << endl;

    //使用遍歷的方式進行處理
    TiXmlNode* child = NULL;
    TiXmlElement* element = NULL;
    TiXmlAttribute *attr = NULL;
    int ct;
    while(child = doc.FirstChild()->IterateChildren(child))
    {
        cout << child->ValueStr() << "\t";
        ct = child->Type();
        cout << ct << "\t";
        //根據不同的節點型別做相應處理  
        switch(ct)
        {
        case TiXmlNode::TINYXML_TEXT:
            break;
        case TiXmlNode::TINYXML_ELEMENT:
            element = child->ToElement();
            attr = element->FirstAttribute();
            while(attr)
            {
                cout << attr->NameTStr() << "=" << attr->ValueStr() << '\t';
                attr = attr->Next();
            }
            break;
        }
    }

    return 0;
}
寫XML示例程式碼:
void CPrintObj::WriteXML(CString &path)
{
	TiXmlDocument* xmlDoc = new TiXmlDocument();
	TiXmlElement element("AutoDraw");
	TiXmlNode* rootNode = xmlDoc->InsertEndChild(element);

	TiXmlNode* curNode = InsertNode(rootNode, "Paper");
	WriteData(curNode, "Width", m_papp_data->ppaper_data->width);
	WriteData(curNode, "Height", m_papp_data->ppaper_data->height);
	WriteData(curNode, "FilterType", m_papp_data->m_nFileterType);
	WriteData(curNode, "A0A1Lengthen", m_papp_data->m_nSelA0A1Length);
	WriteData(curNode, "Code", m_papp_data->ppaper_data->code);
	WriteData(curNode, "AutoDrawType", m_papp_data->auto_draw_style);
	WriteData(curNode, "XCleanrance", m_papp_data->m_nXDrawingCleanrance);
	WriteData(curNode, "YCleanrance", m_papp_data->m_nYDrawingCleanrance);
	WriteText(curNode, "OutputPath", m_szUserSaveFilePath.GetBuffer());
	WriteData(curNode, "IsSavePDF", m_papp_data->m_nPDF);

	list_head *auto_draw_iter = &m_papp_data->auto_draw_list;
	while((auto_draw_iter = auto_draw_iter->next) != &m_papp_data->auto_draw_list)
	{
		curNode = InsertNode(rootNode, "Page");
		struct auto_draw_data *pauto_draw_data = list_entry(auto_draw_iter, struct auto_draw_data, list);
		WriteText(curNode, "FilePath", pauto_draw_data->filename);
		WriteData(curNode, "Width", pauto_draw_data->width);
		WriteData(curNode, "Height", pauto_draw_data->height);
		WriteData(curNode, "Code", pauto_draw_data->code);
		WriteData(curNode, "A0A1Lengthen", pauto_draw_data->m_nA0A1_Lenthen);
	}
	char* filePath = WcharToChar(path);
	xmlDoc->SaveFile(filePath);

	xmlDoc->Clear();
	delete xmlDoc;
	delete filePath;
}

讀XML示例程式碼:
void CDPrintObj::LoadData()
{
	TCHAR szPath[MAX_PATH];
	GetModuleFileName(NULL, szPath , MAX_PATH);
	CString strFilePath = szPath;
	strFilePath = strFilePath.Left(strFilePath.ReverseFind(_T('\\')));
	CString strXmlFile;
	strXmlFile.Format(_T("%s\\dumpdata.xml"), strFilePath);
	TiXmlDocument *xmlDocument = new TiXmlDocument();
	char strFile[MAX_PATH];
        wstring2string(strXmlFile, strFile);
	xmlDocument->LoadFile(strFile);
        TiXmlHandle handle(xmlDocument);
	swprintf_s(m_papp_data->dump_filename, L"%S", handle.FirstChild("/APP_DATA/DUMP_FILENAME").ToElement()->GetText());
        m_papp_data->new_paper_size.totalheight = atoi( handle.FirstChild("/APP_DATA/TOTALHEIGHT").ToElement()->GetText());

	TiXmlElement *pRootElement = handle.FirstChild("/APP_DATA/DRW_DATA").ToElement();
	TiXmlElement *pElement = pRootElement->FirstChildElement();
	while(pElement)
	{
		drw_data *pdrw_data = new drw_data;
                swprintf_s(pdrw_data->filename, L"%S", pElement->FirstChildElement("FILENAME")->GetText());
		list_add_tail(&pdrw_data->list, &m_papp_data->drw_list);
		TiXmlElement *pSubElement = pElement->FirstChildElement("SHEET_DATA");
		while(pSubElement)
		{
			sheet_data *psheet_data = new struct sheet_data;
			list_add_tail(&psheet_data->list, &pdrw_data->sheet_list);
	                swprintf_s(psheet_data->sheet_name, L"%S", pSubElement->FirstChildElement("SHEET_NAME")->GetText());
			psheet_data->data.width = atoi(pSubElement->FirstChildElement("WIDTH")->GetText());
			psheet_data->data.height = atoi(pSubElement->FirstChildElement("HEIGHT")->GetText());
			psheet_data->data.xpos = atof(pSubElement->FirstChildElement("XPOS")->GetText());
			psheet_data->data.ypos = atof(pSubElement->FirstChildElement("YPOS")->GetText());
			psheet_data->data.code = atoi(pSubElement->FirstChildElement("CODE")->GetText());
			psheet_data->data.flip = atoi(pSubElement->FirstChildElement("FLIP")->GetText()) > 0;
			pSubElement = pSubElement->NextSiblingElement();
		}
		pElement = pElement->NextSiblingElement();
	}
}

三、 TinyXml的程式設計模型

1.類之間的關係

         C++ TinyXML是個解析庫,主要由DOM模型類(TiXmlBase、TiXmlNode、TiXmlAttribute、TiXmlComment、TiXmlDeclaration、TiXmlElement、TiXmlText、TiXmlUnknown)和操作類(TiXmlHandler)構成。如圖所示。


        TiXmlBase:其它類的基類,是個抽象類
        TiXmlNode:表示一個節點,包含節點的一般方法,如訪問自節點、兄弟節點、編輯自身、編輯子節點
        TiXmlDocument:表示整個XML文件,不對應其中某個特定的節點。
        TiXmlElement:表示元素節點,可以包含子節點和TiXmlAttribute
        TiXmlComment:表示註釋
        TiXmlDeclaration:表示宣告
        TiXmlText:表示文字節點
        TiXmlUnknown:表示未知節點,通常是出錯了
        TiXmlAttribute:表示一個元素的屬性

        可以看到TinyXml中的註釋comment,宣告declaration,元素element,文字等都是節點Node的子類,也就是說可以把XMl檔案中的各個元素當做節點來處理。Node型別也有到各個子類之間的轉換方法,如ToElement()轉換成元素,ToDocument轉換成文件等。

2.  需要注意的問題

2.1  各類之間的轉換

       由 於各個節點類都從TiXmlNode繼承,在使用時常常需要將TiXmlNode*型別的指標轉換為其派生類的指標,在進行這種轉換時,應該首先使用由 TiXmlNode類提供的一系列轉換函式,如ToElement(void),而不是c 的dynamic_cast

2.2 檢查返回值

       由於TinyXml是一個非校驗的解析器,因此當解析一個檔案時,很可能檔案並不包含我們預期的某個節點,在這種情況下,TinyXml將返回空指標。因此,必須要對返回值進行檢查,否則將很容易出現記憶體訪問的錯誤。

四、總結

        TinyXml 最大的特點就是它很小,可以很方便的靜態連線到程式裡。對於像配置檔案、簡單的資料檔案這類檔案的解析,它很適合。但是由於它是非驗證的,因此需要在程式 裡做許多檢查工做,加重了程式編寫的負擔。因此對於複雜的XML檔案,我覺得最好還是用驗證的解析器來處理。