<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                #(60):使用 DOM 處理 XML DOM 是由 W3C 提出的一種處理 XML 文檔的標準接口。Qt 實現了 DOM Level 2 級別的不驗證讀寫 XML 文檔的方法。 與[上一章](http://www.devbean.net/2013/07/qt-study-road-2-read-xml-with-stream/)所說的流的方式不同,DOM 一次性讀入整個 XML 文檔,在內存中構造為一棵樹(被稱為 DOM 樹)。我們能夠在這棵樹上進行導航,比如移動到下一節點或者返回上一節點,也可以對這棵樹進行修改,或者是直接將這顆樹保存為硬盤上的一個 XML 文件。考慮下面一個 XML 片段: ~~~ <doc> <quote>Scio me nihil scire</quote> <translation>I know that I know nothing</translation> </doc> ~~~ 我們可以認為是如下一棵 DOM 樹: ~~~ Document |--Element(doc) |--Element(quote) | |--Text("Scio me nihil scire") |--Element(translation) |--Text("I know that I know nothing") ~~~ 上面所示的 DOM 樹包含了不同類型的節點。例如,Element 類型的節點有一個開始標簽和對應的一個結束標簽。在開始標簽和結束標簽之間的內容作為這個 Element 節點的子節點。在 Qt 中,所有 DOM 節點的類型名字都以 QDom 開頭,因此,`QDomElement`就是 Element 節點,`QDomText`就是 Text 節點。不同類型的節點則有不同類型的子節點。例如,Element 節點允許包含其它 Element 節點,也可以是其它類型,比如 EntityReference,Text,CDATASection,ProcessingInstruction 和 Comment。按照 W3C 的規定,我們有如下的包含規則: ~~~ [Document] <- [Element] <- DocumentType <- ProcessingInstrument <- Comment [Attr] <- [EntityReference] <- Text [DocumentFragment] | [Element] | [EntityReference] | [Entity] <- [Element] <- [EntityReference] <- Text <- CDATASection <- ProcessingInstrument <- Comment ~~~ 上面表格中,帶有 [] 的可以帶有子節點,反之則不能。 下面我們還是以上一章所列出的?books.xml?這個文件來作示例。程序的目的還是一樣的:用`QTreeWidget`?來顯示這個文件的結構。需要注意的是,由于我們選用 DOM?方式處理 XML,無論是 Qt4 還是 Qt5 都需要在 .pro?文件中添加這么一句: ~~~ QT += xml ~~~ 頭文件也是類似的: ~~~ class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); bool readFile(const QString &fileName); private: void parseBookindexElement(const QDomElement &element); void parseEntryElement(const QDomElement &element, QTreeWidgetItem *parent); void parsePageElement(const QDomElement &element, QTreeWidgetItem *parent); QTreeWidget *treeWidget; }; ~~~ `MainWindow`的構造函數和析構函數和上一章是一樣的,沒有任何區別: ~~~ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setWindowTitle(tr("XML DOM Reader")); treeWidget = new QTreeWidget(this); QStringList headers; headers << "Items" << "Pages"; treeWidget->setHeaderLabels(headers); setCentralWidget(treeWidget); } MainWindow::~MainWindow() { } ~~~ `readFile()`函數則有了變化: ~~~ 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; } QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(&file, false, &errorStr, &errorLine, &errorColumn)) { QMessageBox::critical(this, tr("Error"), tr("Parse error at line %1, column %2: %3") .arg(errorLine).arg(errorColumn).arg(errorStr)); return false; } QDomElement root = doc.documentElement(); if (root.tagName() != "bookindex") { QMessageBox::critical(this, tr("Error"), tr("Not a bookindex file")); return false; } parseBookindexElement(root); return true; } ~~~ `readFile()`函數顯然更長更復雜。首先需要使用`QFile`打開一個文件,這點沒有區別。然后我們創建一個`QDomDocument`對象,代表整個文檔。注意看我們上面介紹的結構圖,Document 是 DOM 樹的根節點,也就是這里的`QDomDocument`;使用其`setContent()`函數填充 DOM 樹。`setContent()`有八個重載,我們使用了其中一個: ~~~ bool QDomDocument::setContent ( QIODevice * dev, bool namespaceProcessing, QString * errorMsg = 0, int * errorLine = 0, int * errorColumn = 0 ) ~~~ 不過,這幾個重載形式都是調用了同一個實現: ~~~ bool QDomDocument::setContent ( const QByteArray & data, bool namespaceProcessing, QString * errorMsg = 0, int * errorLine = 0, int * errorColumn = 0 ) ~~~ 兩個函數的參數基本類似。第二個函數有五個參數,第一個是`QByteArray`,也就是所讀取的真實數據,由`QIODevice`即可獲得這個數據,而`QFile`就是`QIODevice`的子類;第二個參數確定是否處理命名空間,如果設置為 true,處理器會自動設置標簽的前綴之類,因為我們的 XML 文檔沒有命名空間,所以直接設置為 false;剩下的三個參數都是關于錯誤處理。后三個參數都是輸出參數,我們傳入一個指針,函數會設置指針的實際值,以便我們在外面獲取并進行進一步處理。 當`QDomDocument::setContent()`函數調用完畢并且沒有錯誤后,我們調用`QDomDocument::documentElement()`函數獲得一個 Document 元素。如果這個 Document 元素標簽是 bookindex,則繼續向下處理,否則則報錯。 ~~~ void MainWindow::parseBookindexElement(const QDomElement &element) { QDomNode child = element.firstChild(); while (!child.isNull()) { if (child.toElement().tagName() == "entry") { parseEntryElement(child.toElement(), treeWidget->invisibleRootItem()); } child = child.nextSibling(); } } ~~~ 如果根標簽正確,我們取第一個子標簽,判斷子標簽不為空,也就是存在子標簽,然后再判斷其名字是不是 entry。如果是,說明我們正在處理 entry 標簽,則調用其自己的處理函數;否則則取下一個標簽(也就是`nextSibling()`的返回值)繼續判斷。注意我們使用這個 if 只選擇 entry 標簽進行處理,其它標簽直接忽略掉。另外,`firstChild()`和`nextSibling()`兩個函數的返回值都是`QDomNode`。這是所有節點類的基類。當我們需要對節點進行操作時,我們必須將其轉換成正確的子類。這個例子中我們使用`toElement()`函數將`QDomNode`轉換成`QDomElement`。如果轉換失敗,返回值將是空的`QDomElement`類型,其`tagName()`返回空字符串,if 判斷失敗,其實也是符合我們的要求的。 ~~~ void MainWindow::parseEntryElement(const QDomElement &element, QTreeWidgetItem *parent) { QTreeWidgetItem *item = new QTreeWidgetItem(parent); item->setText(0, element.attribute("term")); QDomNode child = element.firstChild(); while (!child.isNull()) { if (child.toElement().tagName() == "entry") { parseEntryElement(child.toElement(), item); } else if (child.toElement().tagName() == "page") { parsePageElement(child.toElement(), item); } child = child.nextSibling(); } } ~~~ 在`parseEntryElement()`函數中,我們創建了一個樹組件的節點,其父節點是根節點或另外一個 entry 節點。接著我們又開始遍歷這個 entry 標簽的子標簽。如果是 entry 標簽,則遞歸調用自身,并且把當前節點作為父節點;否則則調用`parsePageElement()`函數。 ~~~ void MainWindow::parsePageElement(const QDomElement &element, QTreeWidgetItem *parent) { QString page = element.text(); QString allPages = parent->text(1); if (!allPages.isEmpty()) { allPages += ", "; } allPages += page; parent->setText(1, allPages); } ~~~ `parsePageElement()`則比較簡單,我們還是通過字符串拼接設置葉子節點的文本。這與上一章的步驟大致相同。 程序運行結果同上一章一模一樣,這里不再貼出截圖。 通過這個例子我們可以看到,使用 DOM 當時處理 XML 文檔,除了一開始的`setContent()`函數,其余部分已經與原始文檔沒有關系了,也就是說,`setContent()`函數的調用之后,已經在內存中構建好了一個完整的 DOM 樹,我們可以在這棵樹上面進行移動,比如取相鄰節點(`nextSibling()`)。對比上一章流的方式,雖然我們早早關閉文件,但是我們始終使用的是`readNext()`向下移動,同時也不存在`readPrevious()`這樣的函數。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看