当前位置: 首页 > news >正文

Qt内存映射到文件,解决打开大文件占用内存高的问题

前言

在开发桌面项目中经常会遇到打开大文件进行解析后对数据进行处理,如果文件太大,解析后的数据全部放到内存中会占用大量的内存,如果同时打开多个文件就会导致内存暴增,最终可能就会出现内存占满而程序崩溃。
解决这个问题,一个比较常见的方法就是实用内存映射到文件。

内存映射文件是一种将文件映射到内存中的技术,通过将文件的内容映射到内存中的一块地址空间,可以直接在内存中对文件进行读写操作,而不需要通过传统的文件IO操作,这一过程涉及创建文件句柄、建立内存映射对象、映射文件到进程空间以及解除映射等步骤。这种方式可以大大提高文件的读写效率,并且方便进行随机访问。

C++的开源库Boost中,可以使用boost::iostreams::mapped_file类来实现内存映射文件的读写操作。该类提供了简单易用的接口,可以方便地进行文件的读写操作。

本文主要介绍Qt中的QFile来实现内存映射操作。使用也非常简单,但是过程中有些步骤需要注意。
在这里插入图片描述

正文

QFileDevice中提供了一个函数 map 用于现内存映射操作,函数定义如下:

uchar *QFileDevice::map(qint64 offset, qint64 size, QFileDevice::MemoryMapFlags flags = NoOptions)

在这里插入图片描述
使用map进行内存映射,unmap取消内存映射。

应用场景

示例场景一: 音频文件编辑

举一个实际的应用场景。

音频文件编辑功能,解析音频的pcm数据后,如果将数据全部放到内存中,然后对内存数据进行操作,理论上是没问题的,但是如果打开的文件比较大,几百兆甚至几个G,将解析的数据放到内存中就会导致内存暴增,如果同时要编辑多个大文件音频,对电脑内存无疑是个巨大的挑战。这种情况下,使用内存映射到文件的方式就是一种很好的选择。

首先将音频解析(如使用FFmpeg)出PCM数据,将PCM数据直接存储到本地文件中,然后将内存映射到该文件,通过指针去操作文件内容、如读取数据、更新数据等。

用法其实很简单:

QFile m_file;
uchar *m_dataPtr;m_file.setFileName(fileName);
if(m_file.open(QIODevice::ReadWrite)){m_dataPtr = m_file.map(0,m_file.size());//to do ...
}

注意,通过map映射后,m_file一定要是打开的状态,如果关闭文件,再操作m_dataPtr 指针将会报错。而且打开文件的方式必须是QIODevice::ReadWrite类型,否则使用指针去更新内存数据时会导致崩溃。

如果使用完毕,要取消映射也很简单,通过unmap,然后关闭文件即可。

if(m_dataPtr){m_file.unmap(m_dataPtr);m_dataPtr = nullptr;
}
if(m_file.isOpen()){m_file.close();
}

注意,如果是在一个类中需要多次操作,可以将指针和文件对象定义成类的成员变量,这样就可以只打开一次文件,然后整个类都可以使用一个指针来操作。

另外,在映射的时候是指定了映射范围的,如 m_file.map(0,m_file.size());是指定了整个文件的大小空间,当然也可以选择某个区间进行映射,这个自己决定就好。
需要注意的是,如果文件内容有新增或删除,通过映射的指针就无法完成,需要取消映射然后关闭文件后,通过QFile去写入新的文件内容,然后重新做映射。

示例场景二:拷贝文件

文件拷贝,大家都知道可以使用QFile::copy的方式来实现,但是对于大文件的拷贝,在实测中,使用内存映射后,直接进行内存拷贝的方式速度更快一些。

上代码:

bool fastCopyFile(const QString &sourceFilePath, const QString &destFilePath)
{//先做内存映射,再做内存拷贝,这种方式比直接使用 QFile::copy效率更高QFile sourceFile(sourceFilePath);QFile destFile(destFilePath);if (!sourceFile.open(QIODevice::ReadOnly)) return false;if (!destFile.open(QIODevice::ReadWrite)) { // 需要 ReadWrite 权限用于映射sourceFile.close();return false;}// 预分配目标文件大小qint64 fileSize = sourceFile.size();destFile.resize(fileSize);// 映射源文件uchar *srcMap = sourceFile.map(0, fileSize);if (!srcMap) {sourceFile.close();destFile.close();return false;}// 映射目标文件uchar *destMap = destFile.map(0, fileSize);if (!destMap) {sourceFile.unmap(srcMap);sourceFile.close();destFile.close();return false;}// 直接内存拷贝memcpy(destMap, srcMap, fileSize);// 解除映射并关闭文件sourceFile.unmap(srcMap);destFile.unmap(destMap);sourceFile.close();destFile.close();return true;
}

这里其实就是用了内存映射到文件的功能,直接操作指针,进行内存拷贝。

http://www.xdnf.cn/news/1432603.html

相关文章:

  • STM32-FreeRTOS操作系统-任务管理
  • Linux - 进程切换 进程调渡
  • 【Linux】进程信号
  • 第2.7节:多模态大模型之Midjourney
  • AI与低代码时代,自动化测试平台如何选型?主流工具详细对比及选型标准解析
  • github添加SSH密钥
  • vue2 跟 vue3 对比总结
  • 面向机器人系统的虚实迁移强化学习:从仿真训练到真实落地的技术突破
  • 重磅!PS2021 和企业微信 5.0 可直接运行,统信兼容引擎 V3.3.2 全面升级!
  • 提示词工程实战指南:5大技巧大幅提升LLM输出质量
  • 网络安全法合规视角下的安全运维体系建设:关键控制点与实施细节深度解析
  • 【论文阅读】DeepSeek-LV2:用于高级多模态理解的专家混合视觉语言模型
  • 【js】Promise.try VS try-catch
  • Spring Boot数据校验validation实战:写少一半代码,还更优雅!
  • 在线宠物用品|基于vue的在线宠物用品交易网站(源码+数据库+文档)
  • 硬件开发_基于物联网的自动售卖机系统
  • 联邦学习论文分享:GPT-FL: Generative Pre-Trained Model-AssistedFederated Learning
  • Apache 的安装及基本使用
  • MMORPG 游戏战斗系统架构
  • MATLAB矩阵及其运算(一)变量与常量
  • Python 中将 JSON 字符串转为对象的几种方法对比
  • 软件测试面试题【内附超详细面试宝典】
  • 【本地知识库问答系统】MaxKB搭建本地知识库问答系统
  • 低代码开发平台有哪些,中国十大低代码开发平台排名
  • 从零开始的云计算生活——第五十六天,临深履薄,kubernetes模块之etcd备份恢复和集群升级指南
  • Ruoyi-vue-plus-5.x第三篇Redis缓存与分布式技术:3.2 缓存注解与使用
  • 第2章:用户界面与基本监控
  • Ansible 循环、过滤器与判断逻辑
  • 小学一到六年级语文/英语/数学作业出题布置网站源码 支持生成PDF和打印
  • 基金交易量预测比赛_数据分析