fread读取整个文件_qt如何实现大文件的加载和显示
最近研究了下如何用qt的原生控件來加載和顯示大文件(>1G),分享下一些摸索經驗。
下文源碼:
compilelife/loginsight?github.com文件的內存映射
在開始qt部分之前,我們先了解一個概念——文件的內存映射。
我們知道一般讀文件用到的API是fopen/fread/fclose,或者是open/read/close,這種方式都需要內核幫忙作一次拷貝。
linux中有一個函數叫mmap(windows也有類似功能),可以避免這樣的一次拷貝。
請看這幅對比圖(圖片來源:https://www.jianshu.com/p/eece39beee20):
當我們用fread/read時,都是觸發了一個步驟1的read系統調用,然后內核幫忙到磁盤中把請求的文件內容讀取到kernnel buffer,然后再copy回用戶進程空間。
相比,如果用mmap,一開始內核就把整個文件映射到了用戶進程的虛擬內存中;映射過程只是分配了地址空間,并沒有拷貝內存,所以速度快。這一段地址空間在代碼層面看到的就是一塊連續的內存,當代碼訪問這塊內存,如果引發缺頁異常,內核就會加載文件內存到buffer。這樣就減少了一次內存拷貝。
使用mmap對于大文件的加載和顯示有什么好處呢:
- 讀取速度快
- 可以把整個文件當做代碼中一個連續內存區域,直接以const char*訪問,即可以透明地認為整個文件已經加載到進程內,且保存為一個字符串(指針)了。對于代碼設計而言較方便。
Qt里顯示大文件
在Qt里,QFile::map提供了跨平臺的“文件內存映射”支持。所以通過調用QFile::map就可以把文件“加載”為一個const char*字符串使用。
我們知道,在 QPlainTextEdit里,顯示文本一般可以用setPlainText。如果直接把map后的內存傳遞給setPlainText會導致文件的所有內容被讀入內存,這顯然是不行的。
一般對大文件處理方式是“分頁”,也就是一次只加載部分內容。
為了讓用戶感知不到文件被“分頁”了,我們需要處理下,自動加載分頁的內容。具體的做法:
思路
在開始實現前,我們最好有一個清晰的思路,可以建個簡單的模型:
這里,我們把窗口可視區想象成一個固定高度的滑塊,整個滑塊可以在整個文件從頭滑動到尾部——對應用戶從第一行拉動滾動條(右側灰色箭頭)直到最后一行。
為了能減少滾動過程中頻繁觸發讀取文件,可以設置一塊預加載區域,比可見區域大。每次可見區域要滑出預加載區的時候,就觸發一次預加載區的預讀。
在實現上,預加載區域對應的就是setPlainText加載的內容,而可見區域的滾動就直接由QPlainText代為實現了。
于是,要實現大文件的加載和顯示,只要: 1. 預讀內容,通過setPlainText到QPlainTextEdit 2. 處理QPlainTextEdit的滾動事件,在即將滾出預讀區的時候,更新預讀區
當然,說起來容易,做起來還是要處理一些瑣碎事務的。詳見:https://github.com/compilelife/loginsight/blob/master/src/logtextedit.cpp
再談文件的內存映射
當然,如果只是單純地去顯示一個大文件, 直接用常規的文件讀寫API也是可行的。map的優勢還不夠明顯。
實際上,map在這個場景里,真正強大的地方是在于把文件當做“已經加載好的連續字符串”。在加載了大文件后,不可避免地需要做查找、定位等邏輯,這時使用map可同時優化效率和代碼可讀性。
比如,我們要在上面工作的基礎上做全文搜索并定位到匹配行。這時QPlainText的find因為只能搜索預加載內容,無法使用。而基于map,只需要對map后的內存地址,執行strstr按字符串查找,再把查找到的位置前后內容載入可視區即可。
總結
為了基于qt原生控件去高效地顯示大文件,我們用了不少奇技淫巧,把QPlainTextEdit偽裝成了支持大文件的文本框。也許下一步可以試試看用QPlainTextDocumentLayout實現自定義文本框,作更深入地優化。
總結
以上是生活随笔為你收集整理的fread读取整个文件_qt如何实现大文件的加载和显示的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: st语言 数组的常用方法_ST语言编程手
- 下一篇: 群晖docker签到京豆_利用Synol