QT_XML文件操作

QT_XML文件操作

XML(可扩展标记语言)是标准通用标记语言(SGML)的子集。作为一种允许用户自定义标记语言的源语言,它主要用于标记数据和定义数据类型。XML 具备扩展性良好、内容与形式分离、遵循严格语法要求以及保值性良好等显著优点【有且只有一个根元素】。

文章目录

XML数据结构

cpp 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<factory>
 <worker>
  <WNo>cs001</WNo>
  <WName>吴又可</WName>
  <WSex>男</WSex>
  <WEdu>带专</WEdu>
  <WDep>技术部</WDep>
  <WSalay>2000</WSalay>
 </worker>
</factory>

第一行:XML文档声明;它定义 XML 的1.0版本,以及UTF-8编码;

第一行:XML文档根元素,在文档中唯一;

第三行:XML文档元素;

项目配置

.pro文件里面添加QT += xml

一、DOM 方式核心类速查

作用
QDomDocument XML 文档对象,代表整棵树,解析/构建的入口
QDomElement 元素节点,如 <worker>
QDomNode 所有节点的基类(元素、文本、注释、属性等)
QDomNodeList 子节点集合
QDomText 文本节点,即标签之间的内容
QDomAttr 属性节点,如 name="xxx"
QDomComment 注释节点 <!-- -->
QDomProcessingInstruction 处理指令,如 XML 声明 <?xml ...?>
QDomNamedNodeMap 属性集合

二、XML 各节点类型判断方法

遍历节点时经常踩到「空白文本节点」的坑(XML 标签之间的换行/缩进会被当成 text 节点),导致 toElement() 得到空元素。常用判断:

cpp 复制代码
QDomNode node = root.firstChild();
while (!node.isNull()) {
    if (node.isElement()) {            // 只处理元素节点
        QDomElement e = node.toElement();
        QString tag = e.tagName();
        // ...
    }
    node = node.nextSibling();
}

常用判断方法:

  • node.isElement() --- 是否元素节点
  • node.isText() --- 是否文本节点
  • node.isComment() --- 是否注释节点
  • node.isAttr() --- 是否属性节点
  • node.nodeType() / node.nodeName() / node.nodeValue()

注意:也可用 QDomNode::ElementNodeTextNode 等枚举配合 nodeType() 判断。

三、中文乱码问题(重要)

读取/写入含中文的 XML 常见乱码,原因和解决:

  1. 写入时指定 UTF-8 ,用 toUtf8() 写入,且文件头声明 UTF-8:
cpp 复制代码
QString xml = doc.toString();
file.write(xml.toUtf8());   // 不要直接 write(xml.toLocal8Bit())
  1. 读取解析时,setContent 的字符串应使用 UTF-8 字节
cpp 复制代码
QByteArray data = file.readAll();
QString xmlStr = QString::fromUtf8(data);
doc.setContent(xmlStr);
  1. 如果必须兼容 GBK 中文,用 QTextCodec(Qt5):
cpp 复制代码
QTextCodec *codec = QTextCodec::codecForName("GBK");
QString xmlStr = codec->toUnicode(data);

Qt6 已移除 QTextCodec,建议统一用 UTF-8。

四、常用 API 汇总

cpp 复制代码
// 解析
bool ok = doc.setContent(&file, &errorMsg, &errorLine, &errorColumn); // 带错误定位
bool ok = doc.setContent(QString);       // 从字符串解析

// 获取根元素(等价于 doc.firstChildElement())
QDomElement root = doc.documentElement();

// 查找元素
QDomElement e = root.firstChildElement("worker");      // 第一个匹配子元素
QDomElement next = e.nextSiblingElement("worker");     // 下一个同标签兄弟元素
QDomNodeList list = doc.elementsByTagName("worker");   // 文档级按标签名查找全部

// 创建元素/文本/属性
QDomElement el = doc.createElement("name");
QDomText t = doc.createTextNode("张三");
el.appendChild(t);
QDomAttr attr = doc.createAttribute("id");
attr.setValue("1");
el.setAttributeNode(attr);
// 或简写:el.setAttribute("id", "1");

// 读取/修改元素文本
QString name = e.text();              // 取元素文本(含所有子文本拼接)
e.firstChild().setNodeValue("新值"); // 修改文本节点值

// 删除节点
QDomNode old = e.parentNode().removeChild(e);

// 输出
QString xml = doc.toString(4);  // 参数 4 表示缩进空格数,美化输出

五、增删改查常用场景

1. 追加/新增节点

cpp 复制代码
QDomElement root = doc.documentElement();
QDomElement newWorker = doc.createElement("worker");
root.appendChild(newWorker);

2. 修改某一节点文本

cpp 复制代码
QDomNodeList list = doc.elementsByTagName("WName");
if (!list.isEmpty()) {
    QDomElement e = list.at(0).toElement();
    // 方法一:直接改第一个子文本节点
    QDomNode oldText = e.firstChild();
    e.firstChild().setNodeValue("新姓名");
    // 方法二:清空后重建(更安全)
    while (e.hasChildNodes()) e.removeChild(e.firstChild());
    e.appendChild(doc.createTextNode("新姓名"));
}

3. 删除指定节点

cpp 复制代码
QDomNodeList list = doc.elementsByTagName("worker");
for (int i = list.size() - 1; i >= 0; --i) {   // 倒序遍历,避免删除时索引错乱
    QDomNode n = list.at(i);
    if (n.toElement().attribute("id") == "3") {
        n.parentNode().removeChild(n);
    }
}

删除集合节点时必须倒序遍历,否则删一个后面索引全乱了。

4. 属性读写

cpp 复制代码
QString id = e.attribute("id");          // 读
bool has = e.hasAttribute("id");         // 判断是否存在
e.setAttribute("id", "10");              // 写(不存在则自动新建)
e.removeAttribute("id");                 // 删

六、QXmlStreamReader / Writer

DOM 会把整棵树读进内存,大文件(几百 MB)会卡。此时用流式 API:

cpp 复制代码
// 读取
QFile file(path);
file.open(QIODevice::ReadOnly | QIODevice::Text);
QXmlStreamReader reader(&file);
while (!reader.atEnd()) {
    reader.readNext();
    if (reader.isStartElement()) {
        QString name = reader.name().toString();
        if (name == "WNo") {
            QString val = reader.readElementText();
            // 处理 val
        }
    }
}
if (reader.hasError()) {
    qDebug() << reader.errorString() << reader.lineNumber();
}
file.close();
cpp 复制代码
// 写入
QFile out(path);
out.open(QIODevice::WriteOnly | QIODevice::Text);
QXmlStreamWriter writer(&out);
writer.setAutoFormatting(true);      // 自动换行缩进
writer.setAutoFormattingIndent(4);   // 缩进 4 空格
writer.writeStartDocument("1.0", false); // 第二个参数 false 表示不写独立声明,可省 encoding
writer.writeStartElement("factory");
writer.writeStartElement("worker");
writer.writeTextElement("WNo", "cs001"); // 一步写 <WNo>cs001</WNo>
writer.writeEndElement();
writer.writeEndElement();
writer.writeEndDocument();
out.close();

DOM 适合:小文件、需要随机增删改查树结构。Stream 适合:大文件、顺序读写、内存敏感场景。

七、常见坑与注意事项

  1. 空白文本节点firstChild() 可能返回换行文本节点,务必用 isElement()firstChildElement() 过滤。
  2. ReadWrite 打开已有文件不清空 :覆盖写前必须 resize(0)QIODevice::Truncate,否则旧内容残留导致非法 XML。
  3. documentElement() 必须先确保解析成功:setContent 失败时 documentElement 为空,直接访问会崩溃。
  4. 内存泄漏 :Qt DOM 用引用计数自动管理,一般无需手动 delete;ui 对象在析构里 delete 即可。
  5. 编码:XML 头声明编码必须与实际写入字节一致,否则中文乱码。
  6. 关闭文件 :解析/写入完成后记得 close(),否则文件句柄占用,后续操作可能失败。
  7. Qt6 差异QTextCodec 被移除,改用 QStringConvertertoString() 基本用法不变。

八、一个更精简的「读+写」完整示例

cpp 复制代码
#include <QDomDocument>
#include <QFile>

// 写:把一张 map 写入 config.xml
void writeConfig(const QMap<QString, QString> &cfg, const QString &path) {
    QDomDocument doc;
    QDomProcessingInstruction pi = doc.createProcessingInstruction(
        "xml", "version=\"1.0\" encoding=\"UTF-8\"");
    doc.appendChild(pi);

    QDomElement root = doc.createElement("config");
    doc.appendChild(root);

    for (auto it = cfg.begin(); it != cfg.end(); ++it) {
        QDomElement item = doc.createElement("item");
        item.setAttribute("key", it.key());
        item.setAttribute("value", it.value());
        root.appendChild(item);
    }

    QFile file(path);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
        return;
    file.write(doc.toString(4).toUtf8()); // 4 空格缩进美化
    file.close();
}

// 读:把 config.xml 读回 map
QMap<QString, QString> readConfig(const QString &path) {
    QMap<QString, QString> cfg;
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return cfg;

    QDomDocument doc;
    QString err; int line = -1, col = -1;
    if (!doc.setContent(&file, &err, &line, &col)) {
        qDebug() << "XML parse error:" << err << "line" << line << "col" << col;
        file.close();
        return cfg;
    }
    file.close();

    QDomElement root = doc.documentElement();
    for (QDomElement e = root.firstChildElement("item");
         !e.isNull(); e = e.nextSiblingElement("item")) {
        cfg.insert(e.attribute("key"), e.attribute("value"));
    }
    return cfg;
}

九、Qt读取写入配置文件案例

widget.ui

cpp 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>444</width>
    <height>244</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>读取xml文件测试</string>
  </property>
  <widget class="QPushButton" name="writeBtn">
   <property name="geometry">
    <rect>
     <x>70</x>
     <y>190</y>
     <width>101</width>
     <height>31</height>
    </rect>
   </property>
   <property name="text">
    <string>写入XML文件</string>
   </property>
  </widget>
  <widget class="QPushButton" name="readBtn">
   <property name="geometry">
    <rect>
     <x>270</x>
     <y>190</y>
     <width>101</width>
     <height>31</height>
    </rect>
   </property>
   <property name="text">
    <string>读取XML文件</string>
   </property>
  </widget>
  <widget class="QWidget" name="layoutWidget">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>10</y>
     <width>426</width>
     <height>166</height>
    </rect>
   </property>
   <layout class="QHBoxLayout" name="horizontalLayout_13">
    <item>
     <layout class="QFormLayout" name="formLayout">
      <item row="0" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout">
        <item>
         <widget class="QLabel" name="label">
          <property name="text">
           <string>职工编号:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="no"/>
        </item>
       </layout>
      </item>
      <item row="1" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_2">
        <item>
         <widget class="QLabel" name="label_2">
          <property name="text">
           <string>职工姓名:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="name"/>
        </item>
       </layout>
      </item>
      <item row="2" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_4">
        <item>
         <widget class="QLabel" name="label_4">
          <property name="text">
           <string>职工性别:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="sex"/>
        </item>
       </layout>
      </item>
      <item row="3" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_3">
        <item>
         <widget class="QLabel" name="label_3">
          <property name="text">
           <string>职工学历:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="edu"/>
        </item>
       </layout>
      </item>
      <item row="4" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_5">
        <item>
         <widget class="QLabel" name="label_5">
          <property name="text">
           <string>职工部门:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="dep"/>
        </item>
       </layout>
      </item>
      <item row="5" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_6">
        <item>
         <widget class="QLabel" name="label_6">
          <property name="text">
           <string>职工薪资:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="salay"/>
        </item>
       </layout>
      </item>
     </layout>
    </item>
    <item>
     <layout class="QFormLayout" name="formLayout_2">
      <item row="0" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_7">
        <item>
         <widget class="QLabel" name="label_7">
          <property name="text">
           <string>职工编号:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="no_2"/>
        </item>
       </layout>
      </item>
      <item row="1" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_8">
        <item>
         <widget class="QLabel" name="label_8">
          <property name="text">
           <string>职工姓名:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="name_2"/>
        </item>
       </layout>
      </item>
      <item row="2" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_9">
        <item>
         <widget class="QLabel" name="label_9">
          <property name="text">
           <string>职工性别:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="sex_2"/>
        </item>
       </layout>
      </item>
      <item row="3" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_10">
        <item>
         <widget class="QLabel" name="label_10">
          <property name="text">
           <string>职工学历:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="edu_2"/>
        </item>
       </layout>
      </item>
      <item row="5" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_12">
        <item>
         <widget class="QLabel" name="label_12">
          <property name="text">
           <string>职工薪资:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="salay_2"/>
        </item>
       </layout>
      </item>
      <item row="4" column="0">
       <layout class="QHBoxLayout" name="horizontalLayout_11">
        <item>
         <widget class="QLabel" name="label_11">
          <property name="text">
           <string>职工部门:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="dep_2"/>
        </item>
       </layout>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <QDomComment> // Qt所封装的类,专门用来操作XML文件,专门用来操作XML文件,用来存储一些配置数据信息
#include <QDomNodeList> // 子节点集合类
#include <QDir>
#include <QFile>
#include <QDebug>
#include <QMessageBox>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

    QFile m_qfiles;
    QString strcurrentfilepath; // 表示当前文件的路径
    QString strcurrentfilename; //表示当前文件名

public:
    bool openxmlfile(QString filesnamepath); //打开指定的文件
    void readxmlfile(); //读取XML文件
    void writexmlfiles(); // 写入xml文件
    void readrootxml(QDomNodeList sonnodelist); // 读取子节点数据


private slots:
    void on_writeBtn_clicked();
    void on_readBtn_clicked();
};
#endif // WIDGET_H

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

// ========================== 构造函数 ==========================
// 初始化界面,并设置 XML 文件的默认保存路径与文件名
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
  ui->setupUi(this); // 加载 designer 中设计的 UI 界面

  QDir dirs;                          // 可用来检查/创建目录,此处未实际使用
  strcurrentfilepath = "D:/xmlfiles"; // XML 文件所在目录
  strcurrentfilename = "/factoryworkersxml.xml"; // 文件名
}

// ========================== 析构函数 ==========================
// 释放 ui 对象,避免内存泄漏
Widget::~Widget() { delete ui; }


// ========================== 打开 XML 文件 ==========================
// 功能:按指定文件名打开(不存在则自动创建)XML 文件
// 参数:filesnamepath ------ 相对文件名(如 "/factoryworkersxml.xml")
// 返回:true 表示打开成功,false 表示失败
bool Widget::openxmlfile(QString filesnamepath) {
  m_qfiles.setFileName(strcurrentfilepath + filesnamepath); // 拼接完整路径
  // ReadWrite:可读可写;QFile::Text:以文本模式打开
  return m_qfiles.open(QIODevice::ReadWrite | QFile::Text);
}

// ========================== 写入 XML 文件 ==========================
// 功能:将界面输入框中的内容写入 XML 文件
void Widget::writexmlfiles() {
  // 1. 打开(或创建)XML 文件,失败则终止
  if (!openxmlfile(strcurrentfilename)) {
    qDebug() << "打开XML文件失败,请重新检查";
    return;
  }
  qDebug() << "写入:打开XML文件成功,请认真操作XML";

  // 截断文件,清空旧内容。因为 ReadWrite 模式打开已有文件不会清空,若不截断,末尾会残留旧字节,导致新内容 + 旧残留拼成非法 XML,读取时 setContent 解析失败。
  m_qfiles.resize(0);

  // 2. 创建 QDomDocument 对象,用于构建 XML 文档
  QDomDocument domdoct;
  // 创建 XML 头部处理指令(声明版本号与编码方式)
  QDomProcessingInstruction qversion;
  qversion = domdoct.createProcessingInstruction(
      "xml", "version=\"1.0\" encoding=\"UTF-8\"");
  domdoct.appendChild(qversion); // 将头部声明追加到文档中

  // 3. 创建顶层节点 <factory>
  QDomElement domrootelement = domdoct.createElement("factory");
  domdoct.appendChild(domrootelement);

  // 4. 创建父节点 <worker>,并在其下挂接各个子节点
  QDomElement itemrootelement = domdoct.createElement("worker");
  {
    // ---- 节点1:工号 <WNo> ----
    QDomElement node1 = domdoct.createElement("WNo");  // 创建元素节点
    QDomText domtext1 = domdoct.createTextNode("WNO"); // 创建文本节点
    domtext1.setData(ui->no->text());                  // 写入界面输入框的工号
    node1.appendChild(domtext1);        // 将文本节点绑定到元素节点
    itemrootelement.appendChild(node1); // 将元素节点绑定到父节点 <worker>

    // ---- 节点2:姓名 <WName> ----
    QDomElement node2 = domdoct.createElement("WName");
    QDomText domtext2 = domdoct.createTextNode("WName");
    domtext2.setData(ui->name->text()); // 写入界面输入框的姓名
    node2.appendChild(domtext2);
    itemrootelement.appendChild(node2);

    // ---- 节点3:性别 <WSex> ----
    QDomElement node3 = domdoct.createElement("WSex");
    QDomText domtext3 = domdoct.createTextNode("WSex");
    domtext3.setData(ui->sex->text()); // 写入界面输入框的性别
    node3.appendChild(domtext3);
    itemrootelement.appendChild(node3);

    // ---- 节点4:学历 <WEdu> ----
    QDomElement node4 = domdoct.createElement("WEdu");
    QDomText domtext4 = domdoct.createTextNode("WEdu");
    domtext4.setData(ui->edu->text()); // 写入界面输入框的学历
    node4.appendChild(domtext4);
    itemrootelement.appendChild(node4);

    // ---- 节点5:部门 <WDep> ----
    QDomElement node5 = domdoct.createElement("WDep");
    QDomText domtext5 = domdoct.createTextNode("WDep");
    domtext5.setData(ui->dep->text()); // 写入界面输入框的部门
    node5.appendChild(domtext5);
    itemrootelement.appendChild(node5);

    // ---- 节点6:工资 <WSalay> ----
    QDomElement node6 = domdoct.createElement("WSalay");
    QDomText domtext6 = domdoct.createTextNode("WSalay");
    domtext6.setData(ui->salay->text()); // 写入界面输入框的工资
    node6.appendChild(domtext6);
    itemrootelement.appendChild(node6);
  }

  // 5. 将 <worker> 节点挂接到顶层节点 <factory> 下
  domrootelement.appendChild(itemrootelement);

  // 6. 将整棵文档树转成字符串,以 UTF-8 编码写入文件,并关闭文件
  m_qfiles.write(domdoct.toString().toUtf8());
  m_qfiles.close();

  // 7. 提示用户写入成功
  QMessageBox::information(this, "提示", "恭喜你XML写入成功,请查看目录文件",
                           QMessageBox::Yes);
}


// ========================== 读取 XML 文件 ==========================
// 功能:解析并读取 XML 文件内容
void Widget::readxmlfile() {

  if (!openxmlfile(strcurrentfilename)) {
    qDebug() << "读取:打开xml文件失败,请重新检查";
    return;
  }
  qDebug() << "读取:打开XML文件成功";

  QDomDocument docs;
  // 使用带错误信息参数的重载,解析失败时可以输出具体错误行/列,便于排查
  QString errorMsg;
  int errorLine = -1, errorColumn = -1;
  if (!docs.setContent(&m_qfiles, &errorMsg, &errorLine, &errorColumn)) {
    qDebug() << "读取:操作setContent失败,请重新检查";
    qDebug() << "错误信息:" << errorMsg << ",行:" << errorLine
             << ",列:" << errorColumn;
    m_qfiles.close();
    return;
  }
  qDebug() << "读取:操作setContent成功";
  m_qfiles.close(); // 解析完成后关闭文件,避免文件句柄被占用

  // 读取根节点
  QDomElement root = docs.documentElement();

  // 读取第一个父亲节点
  QDomNode node = root.firstChild();

  while (!node.isNull()) {
    QDomNodeList sonlist = node.childNodes();      // 读取子节点集合信息
    QString rootname = node.toElement().tagName(); // 读取父亲节点名称
    if (rootname.compare("worker") == 0) {
      readrootxml(sonlist); // 将子节点集合信息传递到读取子节点数据
    }
    node = node.nextSibling(); // 读取下一个父节点
  }
}


// ========================== 读取子节点数据 ==========================
// 功能:遍历 <worker> 的子节点集合,将各节点文本显示到右侧编辑框
// 参数:sonnodelist ------ <worker> 节点的全部子节点集合
void Widget::readrootxml(QDomNodeList sonnodelist) {
  // 循环遍历子节点集合,逐个取出节点
  for (int sonnode = 0; sonnode < sonnodelist.size(); sonnode++) {
    // 获取当前子节点并转为元素节点
    QDomElement sonelement = sonnodelist.at(sonnode).toElement();

    // 根据节点标签名,将文本内容填入对应的右侧显示框
    if (sonelement.tagName().compare("WNo") == 0) {
      ui->no_2->setText(sonelement.text()); // 职工编号
    } else if (sonelement.tagName().compare("WName") == 0) {
      ui->name_2->setText(sonelement.text()); // 职工姓名
    } else if (sonelement.tagName().compare("WSex") == 0) {
      ui->sex_2->setText(sonelement.text()); // 职工性别
    } else if (sonelement.tagName().compare("WEdu") == 0) {
      ui->edu_2->setText(sonelement.text()); // 职工学历
    } else if (sonelement.tagName().compare("WDep") == 0) {
      ui->dep_2->setText(sonelement.text()); // 职工部门
    } else if (sonelement.tagName().compare("WSalay") == 0) {
      ui->salay_2->setText(sonelement.text()); // 职工薪资
    }
  }
}


// ========================== 槽函数 ==========================
// 点击"写入"按钮时调用,将界面数据写入 XML 文件
void Widget::on_writeBtn_clicked() { writexmlfiles(); }

void Widget::on_readBtn_clicked() {
  readxmlfile(); // 点击"读取XML文件"按钮,解析文件并回显到右侧编辑框
}

效果


相关推荐
xfhuangfu2 小时前
Oracle中建立到CDB和PDB的连接
数据库·oracle·rpc
小小龙学IT2 小时前
DuckDB 深度实战:用 C++ 在进程内跑一个「分析型数据库」
数据库·c++
NineData2 小时前
DTCC 2026 预告|NineData CEO& 创始人叶正盛:面向 AI Agent 的数据库 DevOps 与数据复制实践
数据库·人工智能·数据库开发·devops·ninedata·数据库技术·dtcc
J_bean3 小时前
MySQL 事务是否必须手动开启?
数据库·mysql·数据库事务·自动提交·手动提交
J_bean3 小时前
MySQL InnoDB 如何检测死锁、判定死锁、处理死锁
数据库·mysql·死锁·死锁检测·处理死锁·死锁判定
浪子明X3 小时前
从 MongoDB 文档到关系模型:构建可重跑、可对账的数据迁移流水线
数据库·mongodb·oracle
杜子不疼.3 小时前
国产数据库撑起固井软件自主化:金仓 × 中海油服「海恒 Cemsol」落地解析
数据库
隔窗听雨眠4 小时前
GBase 8s并发控制深度解析:封锁机制、隔离级别与死锁处理全攻略
服务器·数据库·oracle
刀客Doc4 小时前
即时零售不只是多一个渠道,品牌开始重做终端生意
大数据·数据库
就叫飞六吧4 小时前
防抖、幂等、唯一约束三件套 ;
数据库