<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    MDA之路

    MDA,UML,XML,Eclipse及Java相關的Blog
    posts - 53, comments - 494, trackbacks - 0, articles - 2
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    C++中的XML配置文件編程經驗

    Posted on 2008-05-27 19:40 wxb_nudt 閱讀(22930) 評論(8)  編輯  收藏 所屬分類: 實用編程技術
     

    C++中并沒有操作XML文件的標準庫,因此大家需要使用各自熟悉的XML庫來解決XML文件的讀取與寫入。XML的一個重要用途是作為程序的配置文件,存儲程序運行相關的各種數據。本文總結了使用libxml2庫來對XML配置文件進行編程的一些經驗。最后提供了一個封裝好的類CXMLConfig,并詳細說明了該類的功能、使用方法和注意事項。

    閱讀本文所需的技術背景:

    l         C/C++簡單語法;

    l         XML技術,XPATH技術;

    l         C++編譯器知識;

    本文的內容包括:

    l         下載與安裝LIBXML2ICONV

    l         第一個例子程序的編寫、編譯鏈接和運行;

    l         使用XPATH讀出多個配置項的值;

    l         XML的配置文件類CXMLConfig

    l         將配置項寫入XML文件;

    l         CXMLConfig類使用小結;

    閱讀本文之前最好先讀我的上一篇博客C++XML編程經驗――LIBXML2庫使用指南,那一篇專門介紹libxml2庫的使用方法。本文將不會再詳細介紹libxml2的使用,而是集中精力介紹如何存取XML中的數據。

    本文的源代碼是一個VC6的工程,里面包含三個子工程。地址在http://m.tkk7.com/Files/wxb_nudt/XMLConfigFile.rar

    1.       下載與安裝LIBXML2ICONV

    為了方便讀者,這一段原文照抄上一篇博客。

    Libxml2是一個C語言的XML程序庫,可以簡單方便的提供對XML文檔的各種操作,并且支持XPATH查詢,以及部分的支持XSLT轉換等功能。Libxml2的下載地址是http://xmlsoft.org/,完全版的庫是開源的,并且帶有例子程序和說明文檔。最好將這個庫先下載下來,因為這樣可以查看其中的文檔和例子。

    windows版本的的下載地址是http://www.zlatkovic.com/libxml.en.html;這個版本只提供了頭文件、庫文件和dll,不包含源代碼、例子程序和文檔。在文本中,只需要下載libxml2庫、iconv庫和zlib庫就行了(注意,libxml2庫依賴iconvzlib庫,本文中重點關注libxml2iconvzlib不介紹),我使用的版本是libxml2-2.6.30.win32.zipzlib-1.2.3.win32.zipiconv-1.9.2.win32.zip

    在編程的時候,我們使用windows版本的libxml2zlibiconv,將其解壓縮到指定文件夾,例如D:"libxml2-2.6.30.win32D:"zlib-1.2.3.win32以及D:"iconv-1.9.2.win32。事實上,我們知道在windows下面使用頭文件、庫文件和dll是不需要安裝的,它又沒有使用任何需要注冊的組件或者數據庫,只需要告訴編譯器和鏈接器這些資源的位置就可以了。

    注意:要在path變量中加上D:"iconv-1.9.2.win32"bin;D:"zlib-1.2.3.win32"bin;D:"libxml2-2.6.30.win32"bin這三個地址,否則在執行的時候就找不到。或者使用更簡單的方法,把其中的三個dll到拷貝到system32目錄中。

    有兩種方法來編譯鏈接基于libxml2的程序,第一種是在VC環境中設置libinclude路徑,并在link設置中添加libxml2.libiconv.lib;第二種是用編譯器選項告訴編譯器cl.exe頭文件的位置,并用鏈接器選項告訴鏈接器link.exe庫文件的位置,同時在windows環境變量path中添加libxml2bin文件夾的位置,以便于程序運行時可以找到dll(也可以將dll拷貝到system32目錄下)。

    2.       HELLO,XML CONFIG FILE

    本節的源代碼位于項目HelloXml中,使用的xml文件是Helloxml.xml

    在安裝配置好libxml2iconv庫之后,就可以寫一個簡單的程序來讀取XML中的數據了。該XML內容如下:

     <?xml version="1.0" encoding="GB2312" ?>

     <main>20080526</main>

    使用libxml2庫讀取main節點包含的內容,代碼如下:

    xmlChar* LoadConfigFile(const char* szConfigFilename, xmlChar* xszRel)

    {

        xmlDocPtr doc;   //定義解析文檔指針

        xmlNodePtr curNodePtr; //定義結點指針

        doc = xmlReadFile(szConfigFilename,"GB2312",XML_PARSE_RECOVER); //解析文件

        if (doc == NULL )

        {

           fprintf(stderr,"Document not parsed successfully. "n");    

           xmlFreeDoc(doc);

           exit(1);

        }  

        curNodePtr = xmlDocGetRootElement(doc); //確定文檔根元素

        /*檢查確認當前文檔中包含內容*/

        if (curNodePtr == NULL)

        {

           fprintf(stderr,"empty document"n");

           xmlFreeDoc(doc);

           exit(1);

        }

        //讀取xml文檔中的內容并賦值給對象屬性

        xszRel = xmlNodeGetContent(curNodePtr);

        xmlFreeDoc(doc);

        return xszRel;

    }

    int main(int argc, char* argv[])

    {

        xmlChar* xszContent = NULL;

        xszContent = LoadConfigFile("..""Debug""HelloXml.xml",xszContent);

        if (xszContent != NULL)

        {

           cout<<"HELLO, XML CONFIG FILE. content = "<<xszContent<<endl;

           xmlFree(xszContent);

        }

        return 0;

    }

    編譯代碼之前要注意:xml文檔存放的地點不是本項目文件夾,而是項目文件夾上層的Debug目錄,同時將編譯和鏈接的目的文件夾都設置為項目文件夾上層的Debug目錄。第二點,在link選項中加入了libxml2.libiconv.lib。第三點,在系統的Path變量中指明了libxml2.dlliconv.dllzlib1.dll的路徑(為了方便讀者,我將這三個dll都拷貝到了Debug目錄下面)。

    編譯鏈接完畢后運行程序,得到如下結果:

    HELLO, XML CONFIG FILE. content = 20080526

    3.       使用XPATH讀出多個配置項的值

    本節的源代碼位于項目XPathConfig中,使用的xml文件是XPathConfig.xml

    上面的例子中,為了理解的便利僅在根節點中存儲了一個值,而實際的配置文件往往是同時存放多個配置項的值。舉例如下:

    <main>

        <IP>127.0.0.1</IP>

        <Port>80</Port>

    </main>

    Xml中存儲了一個IP地址和一個端口值。其XPATH地址分別是/main/IP//main/Port/。當然,更加復雜的XPATH值也可同樣處理。

    為了方便的操作xml文檔,我寫了一組xml函數,位于Code_Conv.hCode_Conv.cpp中,其功能如下:

    l         openXmlFile,打開Xml文檔,返回文檔指針;

    l         closeXmlFile,關閉Xml文檔;

    l         getXmlString,根據XPATH路徑讀取字符串;

    l         getXmlInt,根據XPATH路徑讀取整型值;

    為了處理中文以及查詢Xpath節點,我還寫了四個被上述函數調用的函數:

    l         code_convert,從一種編碼轉為另一種編碼;

    l         u2g,從UTF-8轉換為GB2312編碼;

    l         g2u,從GB2312轉換為UTF-8編碼;

    l         get_nodeset,調用xpath查詢節點集合,成功則返回xpath的對象指針,失敗返回NULL

    然后,主程序便簡化為:

    int main(int argc, char* argv[])

    {

        xmlDocPtr doc = openXmlFile("..""Debug""XPathConfig.xml");

        string strIP = getXmlString(doc,"/main/IP");

        int iPort = getXmlInt(doc,"/main/Port");

        cout<<"IP = "<<strIP.c_str()<<" Port = "<<iPort<<endl;

        closeXmlFile(doc);

        return 0;

    }

    運行結果為:

    IP = 127.0.0.1 Port = 80

    觀察上面的代碼可以發現,整個主程序幾乎與libxml2庫無關了,除了一個xmlDocPtr變量。再次觀察可以發現,這個變量幾乎出現在每個自定義函數中,它代表的是一種狀態,或者可以稱為屬性。而那些自定義函數可以稱之為功能。因此,按照許多C++專著的說法,屬性+功能=對象。《C++沉思錄》中說道,CC++最大的不同在于,C++擁有一個最合適的存儲程序狀態的位置,即對象的屬性;而C則必須在許多函數中留出一個位置來保存這個狀態。這句話,簡直正確得可怕

    4.       XML的配置文件類CXMLConfig

    本節的源代碼位于項目UseClass中,使用的xml文件還是XPathConfig.xml

    于是有了下面的CXMLConfig類定義:

    class CXMLConfig 

    {

    public:

        CXMLConfig(const char* szXmlFilename);

        ~CXMLConfig();

        //根據XPATH路徑讀取字符串

        string getXmlString(const char *szXpath);

        int getXmlInt(const char* szXpath);   

    private:

        //代碼轉換:從一種編碼轉為另一種編碼  

        int code_convert(char* from_charset, char* to_charset, char* inbuf,int inlen, char* outbuf, int outlen);

        //UNICODE碼轉為GB2312碼  

        //成功則返回一個動態分配的char*變量,需要在使用完畢后手動free,失敗返回NULL

        char* u2g(char *inbuf);

        //GB2312轉為UNICODE   

        //成功則返回一個動態分配的char*變量,需要在使用完畢后手動free,失敗返回NULL

        char* g2u(char *inbuf);

        //調用xpath查詢節點集合,成功則返回xpath的對象指針,失敗返回NULL

        xmlXPathObjectPtr get_nodeset(const xmlChar *xpath);

    private:

        string m_strFilename;

        xmlDocPtr m_doc;

    };

    使用這個類來改寫主程序,可以讓使用者完全脫離libxml2的庫環境,并且省略了打開和關閉xml文件的步驟,因為這些工作在構造和析構函數中完成了。

    int main(int argc, char* argv[])

    {

        CXMLConfig xmlConfig("..""Debug""XPathConfig.xml");

        string strIP = xmlConfig.getXmlString("/main/IP");

        int iPort = xmlConfig.getXmlInt("/main/Port");

        cout<<"IP = "<<strIP.c_str()<<" Port = "<<iPort<<endl;

        return 0;

    }

    運行結果為:

    IP = 127.0.0.1 Port = 80

    5.       將配置項寫入XML文件

    本節的源代碼位于項目UseClass中,使用的xml文件依然是XPathConfig.xml

    目前CXMLConfig類已經有了打開xml文件,讀取數據以及關閉xml文件的功能。還缺少寫入數據的功能。寫入數據功能的算法也很簡單:先將xml文件讀入內存,然后通過xpath找到相應節點,并修改節點內容,最后將內存中的xml文件一次性寫入硬盤。這里有一點要注意,如果在寫入過程中硬盤斷電或者出現其他故障,則會造成無法恢復的錯誤,數據會全部丟失。為了防止這種情況,還應該在寫入前進行數據備份的工作。通盤考慮后,在CXMLConfig類中加入如下函數:

    writeXmlString:將字符串寫入xml文檔相應節點;

    writeXmlInt:將整型寫入xml文檔相應節點;

    saveConfigFile:將內存中的xml文檔寫入硬盤;

    saveBakConfigFile:保存當前的xml文檔到bak文件(即xml文檔名加_BAK.XML)中;

    loadBakConfigFile:將bak文件讀入內存;

    注意,在調用saveConfigFile時會自動調用saveBakConfigFile,將原有配置文件保存為備份文件。修改后的類如下:

    class CXMLConfig 

    {

    public:

        CXMLConfig(const char* szXmlFilename);

        ~CXMLConfig();

        //根據XPATH路徑讀取字符串

        string getXmlString(const char *szXpath);

        int getXmlInt(const char* szXpath);   

        bool writeXmlString(const string strValue, const char* szXpath);

        bool writeXmlInt(const int iValue, const char* szXpath);

        bool saveConfigFile();

        bool saveBakConfigFile();

        bool loadBakConfigFile();

    private:

        //代碼轉換:從一種編碼轉為另一種編碼  

        int code_convert(char* from_charset, char* to_charset, char* inbuf,

                       int inlen, char* outbuf, int outlen);

        //UNICODE碼轉為GB2312碼  

        char* u2g(char *inbuf);

        //GB2312轉為UNICODE  

        char* g2u(char *inbuf);

        //調用xpath查詢節點集合,成功則返回xpath的對象指針,失敗返回NULL

        xmlXPathObjectPtr get_nodeset(const xmlChar *xpath);

        // 禁止拷貝構造函數和"="操作

        CXMLConfig(const CXMLConfig&);

        CXMLConfig& operator=(const CXMLConfig&);

    private:

        string m_strFilename;

        xmlDocPtr m_doc;

    };

    然后我們修改了主程序,其功能為讀出數據后修改了數據,然后存入了配置文件,主程序如下:

    int main(int argc, char* argv[])

    {

        CXMLConfig xmlConfig("..""Debug""XPathConfig.xml");

        string strIP = xmlConfig.getXmlString("/main/IP");

        int iPort = xmlConfig.getXmlInt("/main/Port");

        cout<<"IP = "<<strIP.c_str()<<" Port = "<<iPort<<endl;

        strIP = "127.1.1.1";

        iPort = 81;

        xmlConfig.writeXmlString(strIP,"/main/IP");

        xmlConfig.writeXmlInt(iPort,"/main/Port");

        if(xmlConfig.saveConfigFile())

        {

           cout<<"Save Config file success!"<<endl;

        }

        return 0;

    }

    運行完以后會發現兩個結果,第一個是配置文件XPathConfig.xml中的內容已經被修改,第二個是原配置文件內容備份在XPathConfig_bak.xml中。

    6.       CXMLConfig類使用小結

    目前為止,CXMLConfig類提供了較為便利的讀取和保存XML配置文件的功能。那么使用CXMLConfig需要哪些步驟呢?

    第一,正確安裝了libxml2iconv庫,包括頭文件、lib文件和dll文件。注意頭文件主要是libxml2iconv的頭文件,lib文件就是兩個libxml2.libiconv.lib,而dll有三個,即libxml2.dlliconv.dllzlib1.dll注意:如果你沒有正確安裝,那么無法正確編譯我的例子程序,但是可以運行,因為我已經將dll都包含到運行目錄下

    第二,確信你弄懂了你的xml配置文件結構,并放在正確的地方;

    第三,使用CXMLConfig xmlConfig("..""Debug""XPathConfig.xml")語句正確構造一個CXMLConfig對象,并調用相應的方法來操作xml文件。

    CXMLConfig類使用的注意事項:

    第一,注意xml文件必須使用節點來存儲數據,而不是屬性。若使用屬性來保存數據,CXMLConfig類不會正確讀出其數據,當然更不能正確寫入。若有興趣,可以擴展CXMLConfig類來實現對屬性數據的存取,事實上那非常簡單。

    第二,若有兩個節點的XPATH路徑相同,例如

    <main>

        <IP>127.0.0.1</IP>

    <IP>127.0.0.2</IP>

        <Port>80</Port>

    </main>

    那么使用getXmlString將只會得到第一個節點的內容。同理,寫入時也只會寫入第一個節點。

    CXMLConfig類的使用環境:

    第一,   使用節點來存儲數據;

    第二,   節點的XPATH路徑各不相同;

    第三,   XML文件最好不大于100M

    總之,若有更復雜的要求,請還是仔細研究libxml2或者任意一個開源或商用XML庫。

    7.       文末的話

    事實上,按照原計劃這篇博客才剛剛開頭,后面才是最精彩的部分。其內容是介紹如何將XML文件當作一個小型的數據庫,把多個XPATH路徑相同的鍵和值讀入一個std::map<std::string,std::string>中,然后在程序中方便的使用這個map來查找,存取某一類數據。但是由于前面的部分寫作時考慮得太詳細,而且CXMLConfig類也介紹逐漸趨于完善,因此為了防止喧賓奪主,本文就到這里結束為好。作為一篇libxml2C++的入門文章,恰到好處!


    評論

    # re: C++中的XML配置文件編程經驗[未登錄]  回復  更多評論   

    2008-05-30 09:13 by diego
    師兄近來可好?

    # re: C++中的XML配置文件編程經驗[未登錄]  回復  更多評論   

    2008-10-16 10:27 by walter
    好文章!!!
    受益良多 謝謝

    # re: C++中的XML配置文件編程經驗  回復  更多評論   

    2008-11-19 11:47 by bluebaby
    為什么我在編譯HelloXml時出現這樣的錯誤提示,"error C2146: syntax error : missing ';' before identifier 'UNALIGNED'","fatal error C1004: unexpected end of file found";這個頭文件我也沒修改過,而且我看了一下出錯位置,也沒發現那有什么錯誤,請問是怎么回事?

    # re: C++中的XML配置文件編程經驗  回復  更多評論   

    2009-01-01 19:01 by redhat126
    受益匪淺!
    請問可以將路徑當作變量寫入嗎?
    我發現在寫入'<'時,出現的是&lt;,有什么辦法呢?

    # re: C++中的XML配置文件編程經驗[未登錄]  回復  更多評論   

    2009-01-03 13:16 by wxb_nudt
    @redhat126
    對于很多XML文檔來說, '<'的內部編碼就是&lt;
    所以你要注意編碼的情況。具體的情況很多,要靠自己掌握。@redhat126

    # re: C++中的XML配置文件編程經驗  回復  更多評論   

    2010-02-10 18:09 by neige
    不知前輩后面的部分寫出來了沒有?非常期待,將會對我非常有用。

    # re: C++中的XML配置文件編程經驗  回復  更多評論   

    2011-10-20 08:49 by fff
    d:\win2003\program files\microsoft visual studio\vc98\include\bhtypes.h(19) : error C2146: syntax error : missing ';' before identifier 'UNALIGNED'

    # re: C++中的XML配置文件編程經驗  回復  更多評論   

    2012-03-06 12:02 by gsh
    在用xmllib2 中xpath的節點路徑如果有漢字時就有問題,比如"/root/節點1[position=1]"就查不到應該返回的值,變成沒值了。
    主站蜘蛛池模板: a级毛片在线免费看| 天天看片天天爽_免费播放| 亚洲91精品麻豆国产系列在线| 免费在线看v网址| 五月天国产成人AV免费观看| 亚洲国产精品国自产拍AV| 国产精品视频永久免费播放| 一级做a爰片久久毛片免费陪 | 亚洲色偷偷偷网站色偷一区| 成人免费无码精品国产电影| 国产成人无码区免费网站| 亚洲一卡2卡3卡4卡5卡6卡| 亚洲精品午夜国产VA久久成人| 成人免费无码大片a毛片| CAOPORM国产精品视频免费| 亚洲一区免费视频| 亚洲一区二区三区自拍公司| 在线免费观看色片| 久久精品免费视频观看| 色屁屁在线观看视频免费| 亚洲成av人片在线看片| 亚洲人成网站在线播放vr| 国产美女被遭强高潮免费网站| 一级毛片全部免费播放| 免费无码婬片aaa直播表情| 亚洲五月综合缴情婷婷| 亚洲高清国产AV拍精品青青草原| 日本特黄特黄刺激大片免费| 在线看片免费人成视久网| 国产乱子伦精品免费视频| 亚洲国产精品网站在线播放 | 亚洲精品mv在线观看| 亚洲午夜无码久久久久| 国产a不卡片精品免费观看| 无码精品A∨在线观看免费| 3344在线看片免费| 日韩毛片免费一二三| 亚洲精品精华液一区二区| 亚洲成人动漫在线观看| 亚洲成AV人片在线观看| 国产亚洲成归v人片在线观看 |