<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、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                #(61):使用 SAX 處理 XML 前面兩章我們介紹了使用流和 DOM 的方式處理 XML 的相關內容,本章將介紹處理 XML 的最后一種方式:SAX。SAX 是一種讀取 XML 文檔的標準 API,同 DOM 類似,并不以語言為區別。Qt 的 SAX 類基于 SAX2 的 Java 實現,不過具有一些必要的名稱上的轉換。相比 DOM,SAX 的實現更底層因而處理起來通常更快。但是,我們前面介紹的`QXmlStreamReader`類更偏向 Qt 風格的 API,并且比 SAX 處理器更快,所以,現在我們之所以使用 SAX API,更主要的是為了把 SAX API 引入 Qt。在我們通常的項目中,并不需要真的使用 SAX。 Qt 提供了`QXmlSimpleReader`類,提供基于 SAX 的 XML 處理。同前面所說的 DOM 方式類似,這個類也不會對 XML 文檔進行有效性驗證。`QXmlSimpleReader`可以識別良格式的 XML 文檔,支持 XML 命名空間。當這個處理器讀取 XML 文檔時,每當到達一個特定位置,都會調用一個用于處理解析事件的處理類。注意,這里所說的“事件”,不同于 Qt 提供的鼠標鍵盤事件,這僅是處理器在到達預定位置時發出的一種通知。例如,當處理器遇到一個標簽的開始時,會發出“新開始一個標簽”這個通知,也就是一個事件。我們可以從下面的例子中來理解這一點: ~~~ <doc> <quote>Gnothi seauton</quote> </doc> ~~~ 當讀取這個 XML 文檔時,處理器會依次發出下面的事件: ~~~ startDocument() startElement("doc") startElement("quote") characters("Gnothi seauton") endElement("quote") endElement("doc") endDocument() ~~~ 每出現一個事件,都會有一個回調,這個回調函數就是在稱為 Handler 的處理類中定義的。上面給出的事件都是在`QXmlContentHandler`接口中定義的。為簡單起見,我們省略了一些函數。`QXmlContentHandler`僅僅是眾多處理接口中的一個,我們還有`QXmlEntityResolver`,`QXmlDTDHandler`,`QXmlErrorHandler`,`QXmlDeclHandler`以及`QXmlLexicalHandler`等。這些接口都是純虛類,分別定義了不同類型的處理事件。對于大多數應用程序,`QXmlContentHandler`和`QXmlErrorHandler`是最常用的兩個。 為簡化處理,Qt 提供了一個`QXmlDefaultHandler`。這個類實現了以上所有的接口,每個函數都提供了一個空白實現。也就是說,當我們需要實現一個處理器時,只需要繼承這個類,覆蓋我們所關心的幾個函數即可,無需將所有接口定義的函數都實現一遍。這種設計在 Qt 中并不常見,但是如果你熟悉 Java,就會感覺非常親切。Java 中很多接口都是如此設計的。 使用 SAX API 與`QXmlStreamReader`或者 DOM API 之間最大的區別是,使用 SAX API 要求我們必須自己記錄當前解析的狀態。在另外兩種實現中,這并不是必須的,我們可以使用遞歸輕松地處理,但是 SAX API 則不允許(回憶下,SAX 僅允許一遍讀取文檔,遞歸意味著你可以先深入到底部再回來)。 下面我們使用 SAX 的方式重新解析前面兩章所出現的示例程序。 ~~~ class MainWindow : public QMainWindow, public QXmlDefaultHandler { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); bool readFile(const QString &fileName); protected: bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &attributes); bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName); bool characters(const QString &str); bool fatalError(const QXmlParseException &exception); private: QTreeWidget *treeWidget; QTreeWidgetItem *currentItem; QString currentText; }; ~~~ 注意,我們的`MainWindow`不僅繼承了`QMainWindow`,還繼承了`QXmlDefaultHandler`。也就是說,主窗口自己就是 XML 的解析器。我們重寫了`startElement()`,`endElement()`,`characters()`,`fatalError()`幾個函數,其余函數不關心,所以使用了父類的默認實現。成員變量相比前面的例子也多出兩個,為了記錄當前解析的狀態。 `MainWindow`的構造函數和析構函數同前面沒有變化: ~~~ 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() { } ~~~ 下面來看 readFile() 函數: ~~~ bool MainWindow::readFile(const QString &fileName) { currentItem = 0; QFile file(fileName); QXmlInputSource inputSource(&file); QXmlSimpleReader reader; reader.setContentHandler(this); reader.setErrorHandler(this); return reader.parse(inputSource); } ~~~ 這個函數中,首先將成員變量清空,然后讀取 XML 文檔。注意我們使用了`QXmlSimpleReader`,將`ContentHandler`和`ErrorHandler`設置為自身。因為我們僅重寫了`ContentHandler`和`ErrorHandler`的函數。如果我們還需要另外的處理,還需要繼續設置其它的 handler。`parse()`函數是`QXmlSimpleReader`提供的函數,開始進行 XML 解析。 ~~~ bool MainWindow::startElement(const QString & /*namespaceURI*/, const QString & /*localName*/, const QString &qName, const QXmlAttributes &attributes) { if (qName == "entry") { currentItem = new QTreeWidgetItem(currentItem ? currentItem : treeWidget->invisibleRootItem()); currentItem->setText(0, attributes.value("term")); } else if (qName == "page") { currentText.clear(); } return true; } ~~~ `startElement()`在讀取到一個新的開始標簽時被調用。這個函數有四個參數,我們這里主要關心第三和第四個參數:第三個參數是標簽的名字(正式的名字是“限定名”,qualified name,因此形參是 qName);第四個參數是屬性列表。前兩個參數主要用于帶有命名空間的 XML 文檔的處理,現在我們不關心命名空間。函數開始,如果是 標簽,我們創建一個新的`QTreeWidgetItem`。如果這個標簽是嵌套在另外的 標簽中的,currentItem 被定義為當前標簽的子標簽,否則則是根標簽。我們使用`setText()`函數設置第一列的值,同前面的章節類似。如果是 標簽,我們將 currentText 清空,準備接下來的處理。最后,我們返回 true,告訴 SAX 繼續處理文件。如果有任何錯誤,則可以返回 false 告訴 SAX 停止處理。此時,我們需要覆蓋`QXmlDefaultHandler`的`errorString()`函數來返回一個恰當的錯誤信息。 ~~~ bool MainWindow::characters(const QString &str) { currentText += str; return true; } ~~~ 注意下我們的 XML 文檔。`characters()`僅在 標簽中出現。因此我們在`characters()`中直接追加 currentText。 ~~~ bool MainWindow::endElement(const QString & /*namespaceURI*/, const QString & /*localName*/, const QString &qName) { if (qName == "entry") { currentItem = currentItem->parent(); } else if (qName == "page") { if (currentItem) { QString allPages = currentItem->text(1); if (!allPages.isEmpty()) allPages += ", "; allPages += currentText; currentItem->setText(1, allPages); } } return true; } ~~~ `endElement()`在遇到結束標簽時調用。和`startElement()`類似,這個函數的第三個參數也是標簽的名字。我們檢查如果是 ,則將 currentItem 指向其父節點。這保證了 currentItem 恢復到處理 標簽之前所指向的節點。如果是 ,我們需要把新讀到的 currentText 追加到第二列。 ~~~ bool MainWindow::fatalError(const QXmlParseException &exception) { QMessageBox::critical(this, tr("SAX Error"), tr("Parse error at line %1, column %2:\n %3") .arg(exception.lineNumber()) .arg(exception.columnNumber()) .arg(exception.message())); return false; } ~~~ 當遇到處理失敗的時候,SAX 會回調`fatalError()`函數。我們這里僅僅向用戶顯示出來哪里遇到了錯誤。如果你想看這個函數的運行,可以將 XML 文檔修改為不合法的形式。 我們程序的運行結果同前面還是一樣的,這里也不再贅述了。
                  <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>

                              哎呀哎呀视频在线观看