fread读取整个文件_qt如何实现大文件的加载和显示

最近研究了下如何用qt的原生控件来加载和显示大文件(>1G),分享下一些摸索经验。

下文源码:

compilelife/loginsight​github.com
6d510d1d74eaeba429a484da660fbedb.png

文件的内存映射

在开始qt部分之前,我们先了解一个概念——文件的内存映射。

我们知道一般读文件用到的API是fopen/fread/fclose,或者是open/read/close,这种方式都需要内核帮忙作一次拷贝。

linux中有一个函数叫mmap(windows也有类似功能),可以避免这样的一次拷贝。

请看这幅对比图(图片来源:https://www.jianshu.com/p/eece39beee20):

4f0e2c4cf36870074319d9f4c117eedb.png

当我们用fread/read时,都是触发了一个步骤1的read系统调用,然后内核帮忙到磁盘中把请求的文件内容读取到kernnel buffer,然后再copy回用户进程空间。

相比,如果用mmap,一开始内核就把整个文件映射到了用户进程的虚拟内存中;映射过程只是分配了地址空间,并没有拷贝内存,所以速度快。这一段地址空间在代码层面看到的就是一块连续的内存,当代码访问这块内存,如果引发缺页异常,内核就会加载文件内存到buffer。这样就减少了一次内存拷贝。

使用mmap对于大文件的加载和显示有什么好处呢:

  • 读取速度快
  • 可以把整个文件当做代码中一个连续内存区域,直接以const char*访问,即可以透明地认为整个文件已经加载到进程内,且保存为一个字符串(指针)了。对于代码设计而言较方便。
mmap参考资料: https://www.jianshu.com/p/eece39beee20https://zhuanlan.zhihu.com/p/69555454

Qt里显示大文件

在Qt里,QFile::map提供了跨平台的“文件内存映射”支持。所以通过调用QFile::map就可以把文件“加载”为一个const char*字符串使用。

我们知道,在 QPlainTextEdit里,显示文本一般可以用setPlainText。如果直接把map后的内存传递给setPlainText会导致文件的所有内容被读入内存,这显然是不行的。

一般对大文件处理方式是“分页”,也就是一次只加载部分内容。

为了让用户感知不到文件被“分页”了,我们需要处理下,自动加载分页的内容。具体的做法:

  1. 监听滚动事件,自动加载下一个/上一个分页
  2. 隐藏滚动条,用外部滚动条替代;外部滚动条对应整个文件范围,并保持实时同步

思路

在开始实现前,我们最好有一个清晰的思路,可以建个简单的模型:

7b4df409bee5627eec2d755f6a0bd186.png

这里,我们把窗口可视区想象成一个固定高度的滑块,整个滑块可以在整个文件从头滑动到尾部——对应用户从第一行拉动滚动条(右侧灰色箭头)直到最后一行。

为了能减少滚动过程中频繁触发读取文件,可以设置一块预加载区域,比可见区域大。每次可见区域要滑出预加载区的时候,就触发一次预加载区的预读。

在实现上,预加载区域对应的就是setPlainText加载的内容,而可见区域的滚动就直接由QPlainText代为实现了。

于是,要实现大文件的加载和显示,只要: 1. 预读内容,通过setPlainTextQPlainTextEdit 2. 处理QPlainTextEdit的滚动事件,在即将滚出预读区的时候,更新预读区

当然,说起来容易,做起来还是要处理一些琐碎事务的。详见:https://github.com/compilelife/loginsight/blob/master/src/logtextedit.cpp

再谈文件的内存映射

当然,如果只是单纯地去显示一个大文件, 直接用常规的文件读写API也是可行的。map的优势还不够明显。

实际上,map在这个场景里,真正强大的地方是在于把文件当做“已经加载好的连续字符串”。在加载了大文件后,不可避免地需要做查找、定位等逻辑,这时使用map可同时优化效率和代码可读性。

比如,我们要在上面工作的基础上做全文搜索并定位到匹配行。这时QPlainTextfind因为只能搜索预加载内容,无法使用。而基于map,只需要对map后的内存地址,执行strstr按字符串查找,再把查找到的位置前后内容载入可视区即可。

总结

为了基于qt原生控件去高效地显示大文件,我们用了不少奇技淫巧,把QPlainTextEdit伪装成了支持大文件的文本框。也许下一步可以试试看用QPlainTextDocumentLayout实现自定义文本框,作更深入地优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434114.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[转]listview中设置背景图片后 拉动变黑

经本人亲测有效...在Android中,ListView是最常用的一个控件,在做UI设计的时候,很多人希望能够改变一下它的背景,使他能够符合整体的UI设计,改变背景背很简单只需要准备一张图片然后指定属性 android:background"d…

编译pjsip2.1.0 vidgui程序时,xlib保错问题

/usr/lib/i386-linux-gnu/libX11.so.6: error adding symbols: DSO missing from command line 以上为错误提示,为链接阶段未指定正确的库文件导致; 解决办法: 修改pro文件,增加下面这一行 LIB -lX11 -L/usr/lib/i386-linux-g…

linux内核编译及添加系统调用(hdu)_浅谈关于Linux内核write系统调用操作的原子性

Linux系统的write调用到底是不是原子的。网上能搜出一大堆文章,基本上要么是翻译一些文献,要么就是胡扯,本文中我来结合实例来试着做一个稍微好一点的回答。先摆出结论吧。结论包含两点,即write调用不能保证什么以及write调用能保…

java 判断对象为控制_Java流程控制

Java流程控制1、Scanner对象①java.util.Scanner是Java5的新特性,可以通过Scanner类来获取用户的输入。②基本语法:1 Scanner snew Scanner(System.in);③通过next()和nextLine()方法接受用户输入,通过hasNext()和hasNextLine()方法来判断用户…

lsb_release -a 查询系统版本

LSB是Linux Standard Base的缩写,lsb_release命令用来显示LSB和特定版本的相关信息。如果使用该命令时不带参数,则默认加上-v参数。 -v, --version 显示版本信息 -i, --id 显示发行版的ID -d, --description 显示该发行版的描述信息 -r, --release 显示当…

directx最终用户运行时_运维定位服务故障时,前5分钟都在忙啥?

遇到服务器故障,问题出现的原因很少可以一下就想到。我们基本上都会从以下步骤入手,这些也是绝大多数运维工程师在定位故障时前几分钟的主要排查点:一、尽可能搞清楚问题的前因后果不要一下子就扎到服务器前面,你需要先搞明白对这…

Hadoop源代码分析(二)

下面给出了Hadoop的包的功能分析。 Package Dependences tool 提供一些命令行工具,如DistCp,archive mapreduce Hadoop的Map/Reduce实现 filecache 提供HDFS文件的本地缓存,用于加快Map/Reduce的数据访问速度 fs 文件系统的抽象&…

java同步锁如何使用_java 同步锁(synchronized)的正确使用姿势

关于线程安全,线程锁我们经常会用到,但你的使用姿势正确不,反正我用错了好长一段时间而不自知。所以有了这篇博客总结下线程锁的正确打开姿势废话不说看例子一,对整个方法进行加锁1,对整个方法进行加锁,不同…

IDE--ubuntu下安装 Source insight

2013-06-03 09:05 74人阅读 评论(0) 收藏 举报 习惯了在source insight下编辑阅读源码,在linux下用vi总是用不好 ,还是在ubuntu上用回熟悉的source insight。 在ubuntu中,安装windows程序用wine,然后用wine安装windows软件即可。…

python中什么是数据驱动_利用Python如何实现数据驱动的接口自动化测试

前言 大家在接口测试的过程中,很多时候会用到对CSV的读取操作,本文主要说明Python3对CSV的写入和读取。下面话不多说了,来一起看看详细的介绍吧。 1、需求 某API,GET方法,token,mobile,email三个参数 token为必填项 mo…

密码锁 java接口_从synchronized和lock区别入手聊聊java锁机制

写这篇文章之前,我去百度了一下啥叫锁,百度百科上写道:置于可启闭的器物上,以钥匙或暗码开启。确实我们一般理解的锁就是门锁,密码锁,但是在计算机科学中,锁又是啥,说实话&#xff0…

对cookie和子cookie操作的封装

1 /**2 * 封装cookie的操作3 * type {Object}4 */5 var CookieUtil {6 /**7 * 根据cookie的名字获取相应的值8 * param name cookie名字9 * return {*}10 */11 get:function (name) {12 //对name进行URL编码13 var cookieName…

解决Ubuntu下切换到root用户后没有声音问题

Ubuntu在root用户下,为了安全考虑默认是关闭了声音系统的。 如果要开很简单,因为root登录后pulseaudio没有启动。所以要先启动它 将root加到pulse和pulse-access组: sudo usermod -a -G pulse-access root gpasswd -a root pulse gpasswd -…

lua 给userdata设置元表_lua学习之复习汇总篇

第六日笔记1. 基础概念程序块定义在 lua 中任何一个源代码文件或在交互模式中输入的一行代码程序块可以是任意大小的程序块可以是一连串语句或一条命令也可由函数定义构成,一般将函数定义写在文件中,然后用解释器执行这个文件换行在代码中不起任何作用&a…

java代码请求2次_Android基于OkHttpUtils网络请求的二次封装

OkHttpUtils网络请求为什么进行二次封装?1、减少代码量2、后期换网络处理框架方便二次封装的实现原理1、将网络请求提取在一个方法中2、对里面的可变参数,可以通过参数传递过去,也可以提供一个set方法传递过去3、对于请求失败和成功,我们可以使用接口回调,让调用该方…

集群服务负载均衡------LVS

个人的理解,以一种通俗易懂的方式讲述出来,如果有哪些地方说的不正确的话,希望大家留言指出来。笔者会非是常的感谢! Cluster服务器集群,直接理解为一些单一的服务器的集合通过某种方式组合起来,为客户端提…

Cubieboard2 debian

环境准备 本文所使用的主机环境为kubuntu 12.10,然而一般情况下,下面涉及到的命令对基于Debian的(X)ubuntu系列都应该适用。 为不引起混淆,我们作如下约定: 工作目录为 $WORK_DIR,目标系统 rootfs 目录为 $WORK_DIR/$ROOTFS_DIR命…

linux和python的关系_Python、Linux与我的缘分

是在大二时期,那时候不懂什么技术,所以就选择了 Ubuntu 来学习、 使用, 它好操作、 界面绚丽、 简单易用, 对于我这种 Linux 新手来说知足了。 毕竟没玩过 Linux ,知识有限, 玩不转 Linux 的种种配置&#…

linux 磁盘uuid获取

ls -l /dev/disk/by-uuid/总用量 0lrwxrwxrwx 1 root root 10 2012-08-15 09:28 0af9bc87-c3c9-49eb-829e-caf572298cc7 -> http://www.cnblogs.com/sdb1lrwxrwxrwx 1 root root 10 2012-08-15 09:27 3e8b5c85-3f5b-4864-b45e-03ff0073eb5f -> http://www.cnblogs.com/sd…

tomcat jsp导入java_[导入]Tomcat JSP Web 开发中的乱码问题小姐

1. 静态页面的乱码问题文件的编码和浏览器要显示的编码不一致。1) 检查文件原始的编码, 可以用记事本打开, 然后选择另存为来看;2) 给当前页面加入一个指令来建议浏览器用指定的编码来显示文件字符内容.3) 如果系统是英文XP,没装东亚字符集支持, 也会显示乱码.2. JSP 页面的乱码…