Qt 5.9 XML文件写入指南

发布于:2025-07-04 ⋅ 阅读:(13) ⋅ 点赞:(0)

Qt 5.9 XML文件写入指南


在Qt 5.9中,有多种方法可以编写XML文件。下面我将介绍三种主要方法,并提供完整的代码示例和最佳实践。

三种XML写入方法对比

方法 优点 缺点 适用场景
QXmlStreamWriter 高效、内存占用低 无树形结构 大型XML文件
QDomDocument 树形结构、易操作 内存占用高 小型XML文件、需要频繁修改
Qt XML Patterns XQuery支持 复杂、学习曲线陡 需要XQuery/XPath

方法1:使用QXmlStreamWriter(推荐)

这是最高效的方法,特别适合大型XML文件:

#include <QXmlStreamWriter>
#include <QFile>
#include <QDebug>
#include <QDateTime>

bool writeXmlWithStreamWriter(const QString &filename)
{
    // 使用QSaveFile确保原子写入(防掉电丢失)
    QSaveFile file(filename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qWarning() << "无法打开文件:" << file.errorString();
        return false;
    }

    QXmlStreamWriter xml(&file);
    xml.setAutoFormatting(true);  // 自动格式化
    xml.setAutoFormattingIndent(2); // 缩进2个空格

    // 开始文档
    xml.writeStartDocument();
    
    // 根元素
    xml.writeStartElement("Data");
    xml.writeAttribute("version", "1.0");
    xml.writeAttribute("created", QDateTime::currentDateTime().toString(Qt::ISODate));
    
    // 写入注释
    xml.writeComment("这是一个使用QXmlStreamWriter生成的XML文件");
    
    // 书籍信息
    xml.writeStartElement("Books");
    for (int i = 1; i <= 3; ++i) {
        xml.writeStartElement("Book");
        xml.writeAttribute("id", QString::number(i));
        
        xml.writeTextElement("Title", QString("Qt编程指南 %1").arg(i));
        xml.writeTextElement("Author", QString("作者 %1").arg(i));
        xml.writeTextElement("Year", QString::number(2015 + i));
        xml.writeTextElement("Price", QString::number(35.5 + i));
        
        // 添加CDATA部分
        xml.writeStartElement("Description");
        xml.writeCDATA(QString("<p>这是第%1本书的描述,包含<strong>特殊字符</strong></p>").arg(i));
        xml.writeEndElement(); // Description
        
        xml.writeEndElement(); // Book
    }
    xml.writeEndElement(); // Books
    
    // 用户信息
    xml.writeStartElement("Users");
    xml.writeEmptyElement("User"); // 空元素
    xml.writeAttribute("id", "1001");
    xml.writeAttribute("name", "张三");
    xml.writeAttribute("role", "管理员");
    
    xml.writeEndElement(); // Users
    
    // 结束文档
    xml.writeEndElement(); // Data
    xml.writeEndDocument();

    // 提交文件(原子操作)
    if (!file.commit()) {
        qCritical() << "文件提交失败:" << file.errorString();
        return false;
    }
    
    qDebug() << "XML文件已成功写入:" << filename;
    return true;
}

方法2:使用QDomDocument(DOM方式)

适合小型XML文件或需要频繁修改的场景:

#include <QDomDocument>
#include <QFile>
#include <QDebug>

bool writeXmlWithDomDocument(const QString &filename)
{
    // 创建DOM文档
    QDomDocument doc("XML_DOC");
    
    // 创建处理指令
    QDomProcessingInstruction pi = doc.createProcessingInstruction(
        "xml", "version=\"1.0\" encoding=\"UTF-8\"");
    doc.appendChild(pi);
    
    // 创建根元素
    QDomElement root = doc.createElement("Data");
    root.setAttribute("version", "1.0");
    doc.appendChild(root);
    
    // 创建注释
    QDomComment comment = doc.createComment("这是一个使用QDomDocument生成的XML文件");
    root.appendChild(comment);
    
    // 创建书籍元素
    QDomElement booksElement = doc.createElement("Books");
    root.appendChild(booksElement);
    
    for (int i = 1; i <= 3; ++i) {
        QDomElement bookElement = doc.createElement("Book");
        bookElement.setAttribute("id", i);
        booksElement.appendChild(bookElement);
        
        QDomElement title = doc.createElement("Title");
        title.appendChild(doc.createTextNode(QString("Qt高级编程 %1").arg(i)));
        bookElement.appendChild(title);
        
        QDomElement author = doc.createElement("Author");
        author.appendChild(doc.createTextNode(QString("专家 %1").arg(i)));
        bookElement.appendChild(author);
        
        QDomElement year = doc.createElement("Year");
        year.appendChild(doc.createTextNode(QString::number(2018 + i)));
        bookElement.appendChild(year);
    }
    
    // 创建用户元素
    QDomElement usersElement = doc.createElement("Users");
    root.appendChild(usersElement);
    
    QDomElement userElement = doc.createElement("User");
    userElement.setAttribute("id", "1002");
    userElement.setAttribute("name", "李四");
    userElement.setAttribute("role", "编辑");
    usersElement.appendChild(userElement);
    
    // 写入文件
    QSaveFile file(filename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qWarning() << "无法打开文件:" << file.errorString();
        return false;
    }
    
    // 使用缩进格式输出
    QTextStream out(&file);
    out.setCodec("UTF-8");
    doc.save(out, 2); // 缩进2个空格
    
    if (!file.commit()) {
        qCritical() << "文件提交失败:" << file.errorString();
        return false;
    }
    
    qDebug() << "XML文件已成功写入:" << filename;
    return true;
}

方法3:使用Qt XML Patterns(XQuery)

适合需要复杂XML转换的场景:

#include <QXmlQuery>
#include <QXmlFormatter>
#include <QBuffer>
#include <QFile>
#include <QDebug>

bool writeXmlWithXmlPatterns(const QString &filename)
{
    // 创建XML查询
    QXmlQuery query(QXmlQuery::XQuery10);
    
    // 定义XQuery生成XML
    QString xquery = R"(
        declare variable $books external;
        <Data version="1.0">
            <Books>
            {
                for $book in $books/Book
                return $book
            }
            </Books>
            <Users>
                <User id="1003" name="王五" role="读者"/>
            </Users>
        </Data>
    )";
    
    // 绑定变量
    QBuffer booksData;
    booksData.open(QIODevice::ReadWrite);
    booksData.write(R"(
        <Books>
            <Book id="4">
                <Title>Qt多线程编程</Title>
                <Author>赵六</Author>
                <Year>2022</Year>
            </Book>
            <Book id="5">
                <Title>QML高级技巧</Title>
                <Author>钱七</Author>
                <Year>2023</Year>
            </Book>
        </Books>
    )");
    booksData.seek(0);
    
    query.bindVariable("books", &booksData);
    query.setQuery(xquery);
    
    if (!query.isValid()) {
        qWarning() << "无效的XQuery";
        return false;
    }
    
    // 执行查询并格式化输出
    QBuffer outputBuffer;
    outputBuffer.open(QIODevice::ReadWrite);
    
    QXmlFormatter formatter(query, &outputBuffer);
    formatter.setIndentationDepth(2);
    
    if (!query.evaluateTo(&formatter)) {
        qWarning() << "XQuery执行失败";
        return false;
    }
    
    outputBuffer.seek(0);
    
    // 写入文件
    QSaveFile file(filename);
    if (!file.open(QIODevice::WriteOnly)) {
        qWarning() << "无法打开文件:" << file.errorString();
        return false;
    }
    
    file.write(outputBuffer.data());
    
    if (!file.commit()) {
        qCritical() << "文件提交失败:" << file.errorString();
        return false;
    }
    
    qDebug() << "XML文件已成功写入:" << filename;
    return true;
}

使用示例

#include <QCoreApplication>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    QString filename = "example.xml";
    
    // 方法1:使用QXmlStreamWriter
    if (writeXmlWithStreamWriter("stream_writer.xml")) {
        qDebug() << "方法1成功";
    }
    
    // 方法2:使用QDomDocument
    if (writeXmlWithDomDocument("dom_document.xml")) {
        qDebug() << "方法2成功";
    }
    
    // 方法3:使用Qt XML Patterns
    if (writeXmlWithXmlPatterns("xml_patterns.xml")) {
        qDebug() << "方法3成功";
    }
    
    return a.exec();
}

最佳实践

  1. 文件安全写入

    • 使用QSaveFile而不是QFile,确保写入操作的原子性
    • 避免写入过程中断电导致文件损坏
  2. 编码处理

    // 确保使用UTF-8编码
    xml.writeStartDocument("1.0", true); // 第二个参数表示使用UTF-8
    
  3. 错误处理

    if (xml.hasError()) {
        qWarning() << "XML写入错误:" << xml.errorString();
    }
    
  4. 验证XML结构

    // 在开发阶段验证XML
    #include <QXmlSchemaValidator>
    
    bool validateXml(const QString &filename, const QString &schemaFile) {
        QXmlSchema schema;
        if (!schema.load(schemaFile)) 
            return false;
        
        QXmlSchemaValidator validator(schema);
        return validator.validate(filename);
    }
    
  5. 性能优化

    • 对于大型文件,使用QXmlStreamWriter
    • 避免在循环中频繁打开/关闭文件
    • 使用缓存机制批量写入数据

生成的XML示例

<?xml version="1.0" encoding="UTF-8"?>
<Data version="1.0" created="2023-08-15T14:30:45Z">
    <!--这是一个使用QXmlStreamWriter生成的XML文件-->
    <Books>
        <Book id="1">
            <Title>Qt编程指南 1</Title>
            <Author>作者 1</Author>
            <Year>2016</Year>
            <Price>36.5</Price>
            <Description><![CDATA[<p>这是第1本书的描述,包含<strong>特殊字符</strong></p>]]></Description>
        </Book>
        <Book id="2">
            <Title>Qt编程指南 2</Title>
            <Author>作者 2</Author>
            <Year>2017</Year>
            <Price>37.5</Price>
            <Description><![CDATA[<p>这是第2本书的描述,包含<strong>特殊字符</strong></p>]]></Description>
        </Book>
        <Book id="3">
            <Title>Qt编程指南 3</Title>
            <Author>作者 3</Author>
            <Year>2018</Year>
            <Price>38.5</Price>
            <Description><![CDATA[<p>这是第3本书的描述,包含<strong>特殊字符</strong></p>]]></Description>
        </Book>
    </Books>
    <Users>
        <User id="1001" name="张三" role="管理员"/>
    </Users>
</Data>

常见问题解决

  1. 中文乱码问题

    // 确保使用UTF-8编码
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
    
  2. 文件权限问题

    // 设置正确的文件权限
    file.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
    
  3. 特殊字符处理

    • 使用writeCDATA()处理包含特殊字符的内容
    • 或者使用QString::toHtmlEscaped()进行转义
  4. 大文件内存消耗

    • 使用QXmlStreamWriter代替QDomDocument
    • 分块写入XML文件

在Qt 5.9中,推荐使用QXmlStreamWriter配合QSaveFile进行XML文件写入,这种方法高效、安全且内存占用低,适合大多数应用场景。