最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Qt使用流處理XML文件的示例代碼

 更新時(shí)間:2023年02月03日 10:50:35   作者:音視頻開(kāi)發(fā)老舅  
XML(eXtensible?Markup?Language)是一種通用的文本格式,被廣泛運(yùn)用于數(shù)據(jù)交換和數(shù)據(jù)存儲(chǔ)。本文主要來(lái)和大家聊聊如何使用?Qt?處理?XML?格式的文檔,需要的可以參考一下

本章開(kāi)始我們將了解到如何使用 Qt 處理 XML 格式的文檔。

XML(eXtensible Markup Language)是一種通用的文本格式,被廣泛運(yùn)用于數(shù)據(jù)交換和數(shù)據(jù)存儲(chǔ)(雖然近年來(lái) JSON 盛行,大有取代 XML 的趨勢(shì),但是對(duì)于一些已有系統(tǒng)和架構(gòu),比如 WebService,由于歷史原因,仍舊會(huì)繼續(xù)使用 XML)。XML 由 World Wide Web Consortium(W3C)發(fā)布,作為 SHML(Standard Generalized Markup Language)的一種輕量級(jí)方言。XML 語(yǔ)法類似于 HTML,與后者的主要區(qū)別在于 XML 的標(biāo)簽不是固定的,而是可擴(kuò)展的;其語(yǔ)法也比 HTML 更為嚴(yán)格。遵循 XML 規(guī)范的 HTML 則被稱為 XHTML(不過(guò)這一點(diǎn)有待商榷,感興趣的話可以詳見(jiàn)這里)。

我們說(shuō)過(guò),XML 類似一種元語(yǔ)言,基于 XML 可以定義出很多新語(yǔ)言,比如 SVG(Scalable Vector Graphics)和 MathML(Mathematical Markup Language)。SVG 是一種用于矢量繪圖的描述性語(yǔ)言,Qt 專門提供了 QtSVG 對(duì)其進(jìn)行解釋;MathML 則是用于描述數(shù)學(xué)公式的語(yǔ)言,Qt Solutions 里面有一個(gè) QtMmlWidget 模塊專門對(duì)其進(jìn)行解釋。

另外一面,針對(duì) XML 的通用處理,Qt4 提供了 QtXml 模塊;針對(duì) XML 文檔的 Schema 驗(yàn)證以及 XPath、XQuery 和 XSLT,Qt4 和 Qt5 則提供了 QtXmlPatterns 模塊。Qt 提供了三種讀取 XML 文檔的方法:

  • QXmlStreamReader:一種快速的基于流的方式訪問(wèn)良格式 XML 文檔,特別適合于實(shí)現(xiàn)一次解析器(所謂 “一次解析器”,可以理解成我們只需讀取文檔一次,然后像一個(gè)遍歷器從頭到尾一次性處理 XML 文檔,期間不會(huì)有反復(fù)的情況,也就是不會(huì)讀完第一個(gè)標(biāo)簽,然后讀第二個(gè),讀完第二個(gè)又返回去讀第一個(gè),這是不允許的);
  • DOM(Document Object Model):將整個(gè) XML 文檔讀入內(nèi)存,構(gòu)建成一個(gè)樹(shù)結(jié)構(gòu),允許程序在樹(shù)結(jié)構(gòu)上向前向后移動(dòng)導(dǎo)航,這是與另外兩種方式最大的區(qū)別,也就是允許實(shí)現(xiàn)多次解析器(對(duì)應(yīng)于前面所說(shuō)的一次解析器)。DOM 方式帶來(lái)的問(wèn)題是需要一次性將整個(gè) XML 文檔讀入內(nèi)存,因此會(huì)占用很大內(nèi)存;
  • SAX(Simple API for XML):提供大量虛函數(shù),以事件的形式處理 XML 文檔。這種解析辦法主要是由于歷史原因提出的,為了解決 DOM 的內(nèi)存占用提出的(在現(xiàn)代計(jì)算機(jī)上,這個(gè)一般已經(jīng)不是問(wèn)題了)。

在 Qt4 中,這三種方式都位于 QtXml 模塊中。Qt5 則將 QXmlStreamReader/QXmlStreamWriter 移動(dòng)到 QtCore 中,QtXml 則標(biāo)記為 “不再維護(hù)”,這已經(jīng)充分表明了 Qt 的官方意向。

至于生成 XML 文檔,Qt 同樣提供了三種方式:

  • QXmlStreamWriter,與 QXmlStreamReader 相對(duì)應(yīng);
  • DOM 方式,首先在內(nèi)存中生成 DOM 樹(shù),然后將 DOM 樹(shù)寫入文件。不過(guò),除非我們程序的數(shù)據(jù)結(jié)構(gòu)中本來(lái)就維護(hù)著一個(gè) DOM 樹(shù),否則,臨時(shí)生成樹(shù)再寫入肯定比較麻煩;
  • 純手工生成 XML 文檔,顯然,這是最復(fù)雜的一種方式。

使用 QXmlStreamReader 是 Qt 中最快最方便的讀取 XML 的方法。因?yàn)?QXmlStreamReader 使用了遞增式的解析器,適合于在整個(gè) XML 文檔中查找給定的標(biāo)簽、讀入無(wú)法放入內(nèi)存的大文件以及處理 XML 的自定義數(shù)據(jù)。

每次 QXmlStreamReader 的 readNext() 函數(shù)調(diào)用,解析器都會(huì)讀取下一個(gè)元素,按照下表中展示的類型進(jìn)行處理。我們通過(guò)表中所列的有關(guān)函數(shù)即可獲得相應(yīng)的數(shù)據(jù)值:

類型示例有關(guān)函數(shù)
StartDocumentdocumentVersion(),documentEncoding(),isStandaloneDocument()
EndDocument
StartElement<item>namespaceUri(),name(),attributes(),namespaceDeclarations()
EndElement</item>namespaceUri(),name()
CharactersAT&Ttext(),isWhitespace(),isCDATA()
Comment<!– fix –>text()
DTD<!DOCTYPE …>text(),notationDeclarations(),entityDeclarations(),dtdName(),dtdPublicId(),dtdSystemId()
EntityReferencename(),text()
ProcessingInstruction<?alert?>processingInstructionTarget(),processingInstructionData()
Invalid>&<!error(), errorString()

考慮如下 XML 片段:

<doc>
    <quote>Einmal ist keinmal</quote>
</doc>

一次解析過(guò)后,我們通過(guò) readNext() 的遍歷可以獲得如下信息:

StartDocument
StartElement (name() == "doc")
StartElement (name() == "quote")
Characters (text() == "Einmal ist keinmal")
EndElement (name() == "quote")
EndElement (name() == "doc")
EndDocument

通過(guò) readNext() 函數(shù)的循環(huán)調(diào)用,我們可以使用 isStartElement()、isCharacters() 這樣的函數(shù)檢查當(dāng)前讀取的類型,當(dāng)然也可以直接使用 state() 函數(shù)。

下面我們看一個(gè)完整的例子。在這個(gè)例子中,我們讀取一個(gè) XML 文檔,然后使用一個(gè) QTreeWidget 顯示出來(lái)。我們的 XML 文檔如下:

<bookindex>
    <entry term="sidebearings">
        <page>10</page>
        <page>34-35</page>
        <page>307-308</page>
    </entry>
    <entry term="subtraction">
        <entry term="of pictures">
            <page>115</page>
            <page>244</page>
        </entry>
        <entry term="of vectors">
            <page>9</page>
        </entry>
    </entry>
</bookindex>

首先來(lái)看頭文件:

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
    bool readFile(const QString &fileName);
private:
    void readBookindexElement();
    void readEntryElement(QTreeWidgetItem *parent);
    void readPageElement(QTreeWidgetItem *parent);
    void skipUnknownElement();
 
    QTreeWidget *treeWidget;
    QXmlStreamReader reader;
};

MainWindow 顯然就是我們的主窗口,其構(gòu)造函數(shù)也沒(méi)有什么好說(shuō)的:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    setWindowTitle(tr("XML Reader"));
 
    treeWidget = new QTreeWidget(this);
    QStringList headers;
    headers << "Items" << "Pages";
    treeWidget->setHeaderLabels(headers);
    setCentralWidget(treeWidget);
}
 
MainWindow::~MainWindow()
{
}

接下來(lái)看幾個(gè)處理 XML 文檔的函數(shù),這正是我們關(guān)注的要點(diǎn):

bool MainWindow::readFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::critical(this, tr("Error"),
                              tr("Cannot read file %1").arg(fileName));
        return false;
    }
    reader.setDevice(&file);
    while (!reader.atEnd()) {
        if (reader.isStartElement()) {
            if (reader.name() == "bookindex") {
                readBookindexElement();
            } else {
                reader.raiseError(tr("Not a valid book file"));
            }
        } else {
            reader.readNext();
        }
    }
    file.close();
    if (reader.hasError()) {
        QMessageBox::critical(this, tr("Error"),
                              tr("Failed to parse file %1").arg(fileName));
        return false;
    } else if (file.error() != QFile::NoError) {
        QMessageBox::critical(this, tr("Error"),
                              tr("Cannot read file %1").arg(fileName));
        return false;
    }
    return true;
}

readFile() 函數(shù)用于打開(kāi)給定文件。我們使用 QFile 打開(kāi)文件,將其設(shè)置為 QXmlStreamReader 的設(shè)備。也就是說(shuō),此時(shí) QXmlStreamReader 就可以從這個(gè)設(shè)備(QFile)中讀取內(nèi)容進(jìn)行分析了。接下來(lái)便是一個(gè) while 循環(huán),只要沒(méi)讀到文件末尾,就要一直循環(huán)處理。首先判斷是不是 StartElement,如果是的話,再去處理 bookindex 標(biāo)簽。注意,因?yàn)槲覀兊母鶚?biāo)簽就是 bookindex,如果讀到的不是 bookindex,說(shuō)明標(biāo)簽不對(duì),就要發(fā)起一個(gè)錯(cuò)誤(raiseError())。

如果不是 StartElement(第一次進(jìn)入循環(huán)的時(shí)候,由于沒(méi)有事先調(diào)用 readNext(),所以會(huì)進(jìn)入這個(gè)分支),則調(diào)用 readNext()。為什么這里要用 while 循環(huán),XML 文檔不是只有一個(gè)根標(biāo)簽嗎?直接調(diào)用一次 readNext() 函數(shù)不就好了?這是因?yàn)椋琗ML 文檔在根標(biāo)簽之前還有別的內(nèi)容,比如聲明,比如 DTD,我們不能確定第一個(gè) readNext() 之后就是根標(biāo)簽。正如我們提供的這個(gè) XML 文檔,首先是 聲明,其次才是根標(biāo)簽。如果你說(shuō),第二個(gè)不就是根標(biāo)簽嗎?但是 XML 文檔還允許嵌入 DTD,還可以寫注釋,這就不確定數(shù)目了,所以為了通用起見(jiàn),我們必須用 while 循環(huán)判斷。處理完之后就可以關(guān)閉文件,如果有錯(cuò)誤則顯示錯(cuò)誤。

接下來(lái)看 readBookindexElement() 函數(shù):

void MainWindow::readBookindexElement()
{
    Q_ASSERT(reader.isStartElement() && reader.name() == "bookindex");
    reader.readNext();
    while (!reader.atEnd()) {
        if (reader.isEndElement()) {
            reader.readNext();
            break;
        }
 
        if (reader.isStartElement()) {
            if (reader.name() == "entry") {
                readEntryElement(treeWidget->invisibleRootItem());
            } else {
                skipUnknownElement();
            }
        } else {
            reader.readNext();
        }
    }
}

注意第一行我們加了一個(gè)斷言。意思是,如果在進(jìn)入函數(shù)的時(shí)候,reader 不是 StartElement 狀態(tài),或者說(shuō)標(biāo)簽不是 bookindex,就認(rèn)為出錯(cuò)。然后繼續(xù)調(diào)用 readNext(),獲取下面的數(shù)據(jù)。后面還是 while 循環(huán)。如果是 EndElement,退出,如果又是 StartElement,說(shuō)明是 entry 標(biāo)簽(注意我們的 XML 結(jié)構(gòu),bookindex 的子元素就是 entry),那么開(kāi)始處理 entry,否則跳過(guò)。

那么下面來(lái)看 readEntryElement() 函數(shù):

void MainWindow::readEntryElement(QTreeWidgetItem *parent)
{
    QTreeWidgetItem *item = new QTreeWidgetItem(parent);
    item->setText(0, reader.attributes().value("term").toString());
 
    reader.readNext();
    while (!reader.atEnd()) {
        if (reader.isEndElement()) {
            reader.readNext();
            break;
        }
 
        if (reader.isStartElement()) {
            if (reader.name() == "entry") {
                readEntryElement(item);
            } else if (reader.name() == "page") {
                readPageElement(item);
            } else {
                skipUnknownElement();
            }
        } else {
            reader.readNext();
        }
    }
}

這個(gè)函數(shù)接受一個(gè) QTreeWidgetItem 指針,作為根節(jié)點(diǎn)。這個(gè)節(jié)點(diǎn)被當(dāng)做這個(gè) entry 標(biāo)簽在 QTreeWidget 中的根節(jié)點(diǎn)。我們?cè)O(shè)置其名字是 entry 的 term 屬性的值。然后繼續(xù)讀取下一個(gè)數(shù)據(jù)。同樣使用 while 循環(huán),如果是 EndElement 就繼續(xù)讀取;如果是 StartElement,則按需調(diào)用 readEntryElement() 或者 readPageElement()。由于 entry 標(biāo)簽是可以嵌套的,所以這里有一個(gè)遞歸調(diào)用。如果既不是 entry 也不是 page,則跳過(guò)位置標(biāo)簽。

然后是 readPageElement() 函數(shù):

void MainWindow::readPageElement(QTreeWidgetItem *parent)
{
    QString page = reader.readElementText();
    if (reader.isEndElement()) {
        reader.readNext();
    }
 
    QString allPages = parent->text(1);
    if (!allPages.isEmpty()) {
        allPages += ", ";
    }
    allPages += page;
    parent->setText(1, allPages);
}

由于 page 是葉子節(jié)點(diǎn),沒(méi)有子節(jié)點(diǎn),所以不需要使用 while 循環(huán)讀取。我們只是遍歷了 entry 下所有的 page 標(biāo)簽,將其拼接成合適的字符串。

最后 skipUnknownElement() 函數(shù):

void MainWindow::skipUnknownElement()
{
    reader.readNext();
    while (!reader.atEnd()) {
        if (reader.isEndElement()) {
            reader.readNext();
            break;
        }
 
        if (reader.isStartElement()) {
            skipUnknownElement();
        } else {
            reader.readNext();
        }
    }
}

我們沒(méi)辦法確定到底要跳過(guò)多少位置標(biāo)簽,所以還是得用 while 循環(huán)讀取,注意位置標(biāo)簽中所有子標(biāo)簽都是未知的,因此只要是 StartElement,都直接跳過(guò)。

好了,這是我們的全部程序。只要在 main() 函數(shù)中調(diào)用一下即可:

MainWindow w;
w.readFile("books.xml");
w.show();

然后就能看到運(yùn)行結(jié)果:

以上就是Qt使用流處理XML文件的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Qt處理XML的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語(yǔ)言實(shí)現(xiàn)實(shí)時(shí)鐘表

    C語(yǔ)言實(shí)現(xiàn)實(shí)時(shí)鐘表

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)實(shí)時(shí)鐘表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C++ Boost Array與Unordered使用介紹

    C++ Boost Array與Unordered使用介紹

    Boost是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱。Boost庫(kù)是一個(gè)可移植、提供源代碼的C++庫(kù),作為標(biāo)準(zhǔn)庫(kù)的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開(kāi)發(fā)引擎之一,是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱
    2022-11-11
  • 帶頭結(jié)點(diǎn)的鏈表的基本操作(超詳細(xì))

    帶頭結(jié)點(diǎn)的鏈表的基本操作(超詳細(xì))

    鏈表是一種動(dòng)態(tài)分配空間的存儲(chǔ)結(jié)構(gòu),能更有效地利用存儲(chǔ)空間,通過(guò)對(duì)單鏈表基本操作的代碼實(shí)現(xiàn),我深刻領(lǐng)悟到以“指針”指示元素的后繼,在插入或刪除元素時(shí)不需要移動(dòng)元素
    2023-07-07
  • C++中的單例模式介紹

    C++中的單例模式介紹

    單例模式也稱為單件模式、單子模式,可能是使用最廣泛的設(shè)計(jì)模式。其意圖是保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn),該實(shí)例被所有程序模塊共享
    2013-03-03
  • DEV C++自動(dòng)補(bǔ)全文件頭的設(shè)置操作教程

    DEV C++自動(dòng)補(bǔ)全文件頭的設(shè)置操作教程

    Dev-C++ 是一款輕量級(jí)的集成開(kāi)發(fā)環(huán)境 (IDE),主要用于 C 和 C++ 的程序編寫,它提供了基本的功能來(lái)幫助開(kāi)發(fā)者更高效地工作,其中包括文件頭的自動(dòng)補(bǔ)全功能,本文就給大家介紹了DEV C++自動(dòng)補(bǔ)全文件頭的設(shè)置操作教程,需要的朋友可以參考下
    2025-04-04
  • 一篇文章帶你了解C++中的異常

    一篇文章帶你了解C++中的異常

    這篇文章主要為大家詳細(xì)介紹了C++中的異常,使用數(shù)據(jù)庫(kù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C++ 數(shù)據(jù)結(jié)構(gòu)完全二叉樹(shù)的判斷

    C++ 數(shù)據(jù)結(jié)構(gòu)完全二叉樹(shù)的判斷

    這篇文章主要介紹了C++ 數(shù)據(jù)結(jié)構(gòu)完全二叉樹(shù)的判斷的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C++超詳細(xì)講解稀疏矩陣

    C++超詳細(xì)講解稀疏矩陣

    今天小編就為大家分享一篇關(guān)于C++稀疏矩陣的轉(zhuǎn)置思路并實(shí)現(xiàn)乘法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2022-05-05
  • C語(yǔ)言中編寫可變參數(shù)函數(shù)

    C語(yǔ)言中編寫可變參數(shù)函數(shù)

    這篇文章主要介紹了C語(yǔ)言中編寫可變參數(shù)函數(shù)的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • C語(yǔ)言qsort()函數(shù)的使用方法詳解

    C語(yǔ)言qsort()函數(shù)的使用方法詳解

    qsort是一個(gè)庫(kù)函數(shù),基于快速排序算法實(shí)現(xiàn)的一個(gè)排序的函數(shù),下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言qsort()函數(shù)使用的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06

最新評(píng)論

吉首市| 新余市| 长泰县| 成武县| 济阳县| 房产| 确山县| 宁乡县| 桐乡市| 玛多县| 绵阳市| 天津市| 云霄县| 瑞昌市| 黄龙县| 兰考县| 阳春市| 沙田区| 辽阳市| 广元市| 淮滨县| 嘉峪关市| 广宁县| 独山县| 岱山县| 孟州市| 临泽县| 建水县| 康定县| 东辽县| 泗阳县| 依兰县| 磴口县| 星子县| 河津市| 股票| 东丰县| 陕西省| 呼伦贝尔市| 涡阳县| 肃南|