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();
}
最佳实践
文件安全写入
- 使用
QSaveFile
而不是QFile
,确保写入操作的原子性 - 避免写入过程中断电导致文件损坏
- 使用
编码处理
// 确保使用UTF-8编码 xml.writeStartDocument("1.0", true); // 第二个参数表示使用UTF-8
错误处理
if (xml.hasError()) { qWarning() << "XML写入错误:" << xml.errorString(); }
验证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); }
性能优化
- 对于大型文件,使用
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>
常见问题解决
中文乱码问题
// 确保使用UTF-8编码 QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
文件权限问题
// 设置正确的文件权限 file.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
特殊字符处理
- 使用
writeCDATA()
处理包含特殊字符的内容 - 或者使用
QString::toHtmlEscaped()
进行转义
- 使用
大文件内存消耗
- 使用
QXmlStreamWriter
代替QDomDocument
- 分块写入XML文件
- 使用
在Qt 5.9中,推荐使用QXmlStreamWriter
配合QSaveFile
进行XML文件写入,这种方法高效、安全且内存占用低,适合大多数应用场景。