QXmlStreamWriter的writeCharacters写入大文件到QBuffer时崩溃求解决
问题描述
在Qt 4.8.7(Visual Studio 2010、Windows 10环境)中,使用QXmlStreamWriter通过小缓冲区调用writeCharacters()写入大文件内容时:
- 写入本地XML文件可正常运行
- 写入
QBuffer时writeCharacters()会崩溃
相关代码如下:
测试主函数
#include <QtCore/QCoreApplication> #include <QFile> #include <QXmlStreamWriter> #include <QBuffer> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QByteArray mContentBuffer; QByteArray arr; int pos, filesize; QFile file("C:\\Work\\bigfile.xar"); // 大小:300Mb if(!file.open(QFile::ReadOnly)) { return -1; } mContentBuffer = file.readAll(); file.close(); //QFile profile("C:\\Work\\profile.xml"); //if(!profile.open(QFile::WriteOnly|QFile::Truncate)) //{ // return -1; //} QBuffer buffer; buffer.open(QBuffer::ReadWrite); QXmlStreamWriter stream(&buffer); stream.setAutoFormatting(true); stream.writeStartDocument(); stream.writeStartElement("Profile"); //stream.writeTextElement("Content", mContentBuffer.toBase64()); stream.writeStartElement("Content"); pos = 0; filesize = mContentBuffer.size(); while(pos<filesize){ arr = mContentBuffer.mid(pos, 2000000); stream.writeCharacters(arr.toBase64()); pos+=arr.size(); } stream.writeEndElement(); stream.writeEndElement(); // Profile stream.writeEndDocument(); return 0; }
目标业务函数
bool profileModified() { bool result = true; QFile profile("C:\\Work\\profile.xml"); if(profile.exists()) { QBuffer buffer; buffer.open(QBuffer::ReadWrite); QXmlStreamWriter stream(&buffer); exportProfile(stream); profile.open(QFile::ReadOnly); QByteArray profileArr = profile.readAll(); buffer.seek(0); QByteArray bufferArr = buffer.buffer(); result = (array_compare(profileArr, bufferArr) != 0); profile.close(); } return result; }
解决方案
崩溃原因分析
Qt 4.8.7的QXmlStreamWriter在处理QBuffer这类内存设备时,内部缓冲区管理存在缺陷:当写入超大量数据时,内存缓冲区的扩容逻辑可能触发越界访问或内存分配失败,进而导致崩溃。而写入本地文件时,数据会被及时刷入磁盘,不会在内存中累积过大的缓冲区,因此不会触发该问题。
具体修复方案
禁用自动格式化
自动格式化(setAutoFormatting(true))会额外生成大量空白字符,大幅增加内存占用,是触发崩溃的重要诱因。将其改为stream.setAutoFormatting(false),减少内存压力。优化
QBuffer的初始化方式
提前预分配足够的内存空间给QBuffer,避免频繁扩容:QByteArray bufferData; // 预估算所需空间:原文件Base64编码后体积约为原大小的1.33倍,再加上XML标签的额外开销 bufferData.reserve(mContentBuffer.size() * 2); QBuffer buffer(&bufferData); buffer.open(QBuffer::ReadWrite);改用临时文件替代
QBuffer
如果内存压力仍然过大,可以用临时文件代替内存缓冲区,避免一次性占用大量内存:bool profileModified() { bool result = true; QFile profile("C:\\Work\\profile.xml"); if(profile.exists()) { // 使用临时文件替代QBuffer QTemporaryFile tempFile; if (!tempFile.open()) { return false; } QXmlStreamWriter stream(&tempFile); exportProfile(stream); tempFile.flush(); tempFile.seek(0); profile.open(QFile::ReadOnly); QByteArray profileArr = profile.readAll(); QByteArray tempArr = tempFile.readAll(); result = (array_compare(profileArr, tempArr) != 0); profile.close(); } return result; }升级Qt版本(可选)
该问题在Qt 5.x及以上版本中已被修复,若项目允许,升级到更高版本的Qt可以从根源解决此类内存管理问题。
内容的提问来源于stack exchange,提问作者Ming
相关产品推荐
相关产品推荐

