1. 程式人生 > >Qt XML檔案的建立、讀取介紹以及“重寫XML不能覆蓋原內容問題”解決

Qt XML檔案的建立、讀取介紹以及“重寫XML不能覆蓋原內容問題”解決

簡介XML

XML(Extensible Markup Language,可擴充套件標記語言),是一種類似於HTML的標記語言,設計目的是用來傳輸資料,而不是顯示資料。XML的標籤沒有被預定義,使用者需要在使用時自行進行定義。XML是W3C(全球資訊網聯盟)的推薦標準。相對於資料庫表格的二維表示,XML使用的樹形結構更能表現出資料的包含關係,作為一種文字檔案格式,XML簡單明瞭的特性使得它在資訊儲存和描述領域非常流行。

Qt中提供了Qt XML模組來進行XML文件的處理,這裡主要提供了兩種解析方法: DOM方法,可以進行讀寫;SAX方法,可以進行讀取。但是,從Qt 5開始Qt XML模組不再提供維護,而是推薦使用Qt Core模組中的QXmlStreamReader和QXmlStreamWriter進行XML讀取和寫入,這是一種基於流的方法。

如果要使用Qt XML模組,需要在專案檔案(.pro檔案)中新增QT += xml一行程式碼。

標準的XML文件示例:

<?xml version="1.0" encoding="UTF-8"?>
<library>
    <book id="01">
        <title>Qt</title>
        <author>shiming</author>
    </book>
    <book id="02">
        <title>Linux</title>
        <author>yafei</author>
    </book>
</library>

DOM

DOM(Document Object Model,文件物件模型),是W3C的推薦標準。它提供了一個介面來訪問和改變一個XML檔案的內容和結構,可以將XML文件表示為一個儲存在記憶體中具有層次的樹檢視。文件本身由QDomDocument物件來表示,而文件樹中所有的DOM節點都是QDomNode類的子類。

在Qt中使用QDomProcessingInstruction類來表示XML說明。元素對應QDomElement類。屬性對應QDomAttr類。文字內容由QDomText類表示。所有的DOM節點,比如這裡的說明、元素、屬性和文字等,都使用QDomNode來表示。

下面用一個簡單的例項來介紹XML檔案的建立與讀取。

XML建立

//生成配置檔案
void Login::createConfigXML()
{
    //配置檔案 引數
    QDomDocument doc;
    //根元素
    QDomElement root;
    //介面元素
    QDomElement interface;
    //介面子元素
    QDomElement child;
    //控制元件文字
    QDomText text;

    // 新增處理指令即XML說明
    QDomProcessingInstruction instruction;
    instruction = doc.createProcessingInstruction("xml",
                                                  "version=\"1.0\" encoding=\"UTF-8\"");
    doc.appendChild(instruction);

    // 新增根元素
    root = doc.createElement("色選機軟體系統");
    doc.appendChild(root);


    //【一】管理員介面
    interface = doc.createElement("管理員介面");
    //...【1】
    child = doc.createElement("工作模式");
    text = doc.createTextNode("異色模式");
    child.appendChild(text);
    interface.appendChild(child);
    root.appendChild(interface);

    //【二】背景板介面
    interface = doc.createElement("背景板介面");
    //...【1】
    child = doc.createElement("鏡頭號");
    text = doc.createTextNode("1");
    child.appendChild(text);
    interface.appendChild(child);

    //...【2】
    child = doc.createElement("背景");
    text = doc.createTextNode("紅色");
    child.appendChild(text);
    interface.appendChild(child);

    //新增介面資料
    root.appendChild(interface);

    //建立配置檔案
    QFile file("ColorConfig.xml");

    if(!file.open(QIODevice::Truncate | QIODevice::WriteOnly))
    {
        QMessageBox::about(NULL, tr("錯誤"), tr("生成配置檔案失敗,請重試!"));
        return;
    }

    QTextStream out(&file);
    // 將文件儲存到檔案,4為子元素縮排字元數
    doc.save(out, 4);
    file.close();
}

生成的配置檔案內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<色選機軟體系統>
    <管理員介面>
        <工作模式>異色模式</工作模式>
    </管理員介面>
    <背景板介面>
        <鏡頭號>1</鏡頭號>
        <背景>紅色</背景>
    </背景板介面>    
</色選機軟體系統>

XML讀取

//讀取配置檔案
void Login::readConfigXML()
{
    QFile file("ColorConfig.xml");
    if(!file.exists())
    {
        QMessageBox::about(NULL, tr("提示"), tr("未發現配置檔案"));
        return;
    }
    if (!file.open(QIODevice::ReadOnly))
        return;

    QDomDocument Qdoc;
    if (!Qdoc.setContent(&file))
    {
        file.close();
        return ;
    }
    file.close();

    //【一】管理員介面
    QDomNodeList list = Qdoc.elementsByTagName("管理員介面");
    QDomNodeList child = list.at(0).childNodes();
    for(int i = 0; i < child.count(); ++i)
    {
        //輸出子元素名
        qDebug()<< child.at(i).toElement().tagName() << endl;
        //輸出字元素資料
        qDebug()<< child.at(i).toElement().text()) << endl;
    }

    //【二】背景板介面
    list = Qdoc.elementsByTagName("背景板介面");
    child = list.at(0).childNodes();
    for(int i = 0; i < child.count(); ++i)
    {
        //輸出子元素名
        qDebug()<< child.at(i).toElement().tagName() << endl;
        //輸出字元素資料
        qDebug()<< child.at(i).toElement().text()) << endl;
    }

}

還有另一種方式讀取:

QFile file("ColorConfig.xml");
    if (!file.open(QIODevice::ReadOnly)) return ;
    QDomDocument doc;
    if (!doc.setContent(&file))  {    file.close();  return ; }
    file.close();
    QDomElement docElem = doc.documentElement();
    QDomNode n = docElem.firstChild();
    while(!n.isNull())  {
        if (n.isElement())  {
            QDomElement e = n.toElement();            
            QDomNodeList list = e.childNodes();
            for (int i=0; i<list.count(); i++)  {
                QDomNode node = list.at(i);
                if(node.isElement())
                {
                    //輸出子元素名
                    qDebug()<< node.toElement().tagName() << endl;
                    //輸出字元素資料
                    qDebug()<< node.toElement().text()) << endl;
                 }
        }  n = n.nextSibling();
    }

“重寫XML不能覆蓋原內容問題”解決

如果我們想覆蓋XML中的原資料,重新寫入資料,使用

file.open(QIODevice::Truncate | QIODevice::WriteOnly)

這種方式開啟檔案即可,Truncate 表示重寫,將原檔案資料清空。但是我在做專案中碰到如下問題,重新寫入資料時,原資料沒能覆蓋,新資料也添加了進去,如下:

<?xml version="1.0" encoding="UTF-8"?>
<色選機軟體系統>
    <管理員介面>
        <工作模式>異色模式</工作模式>
    </管理員介面>
    <背景板介面>
        <鏡頭號>1</鏡頭號>
        <背景>紅色</背景>
    </背景板介面>    
</色選機軟體系統>
<?xml version="1.0" encoding="UTF-8"?>
<色選機軟體系統>
    <管理員介面>
        <工作模式>黃白模式</工作模式>
    </管理員介面>
    <背景板介面>
        <鏡頭號>3</鏡頭號>
        <背景>藍色</背景>
    </背景板介面>    
</色選機軟體系統>

最終發現原因:

我的專案中,軟體開啟時,預設載入配置檔案的資料,然而我把讀取、建立XML元素的物件們設定為全域性的了,

    //配置檔案 引數
    QDomDocument doc;
    //根元素
    QDomElement root;
    //介面元素
    QDomElement interface;
    //介面子元素
    QDomElement child;
    //控制元件文字
    QDomText text;

導致軟體開啟時,讀取配置檔案,這些變數都被佔用,並沒有釋放。然後我再去重寫XML內容時,就不可以將已佔用的資料清空替換,導致新的配置檔案資料在原資料下方產生。

解決方法:

將這些讀取、建立XML元素的物件們設定為區域性變數,一次用完自動釋放,不妨礙其他操作。這樣就正確了。