日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

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

發(fā)布時間:2023/12/10 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 fread读取整个文件_qt如何实现大文件的加载和显示 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

最近研究了下如何用qt的原生控件來加載和顯示大文件(>1G),分享下一些摸索經(jīng)驗(yàn)。

下文源碼:

compilelife/loginsight?github.com

文件的內(nèi)存映射

在開始qt部分之前,我們先了解一個概念——文件的內(nèi)存映射。

我們知道一般讀文件用到的API是fopen/fread/fclose,或者是open/read/close,這種方式都需要內(nèi)核幫忙作一次拷貝。

linux中有一個函數(shù)叫mmap(windows也有類似功能),可以避免這樣的一次拷貝。

請看這幅對比圖(圖片來源:https://www.jianshu.com/p/eece39beee20):

當(dāng)我們用fread/read時,都是觸發(fā)了一個步驟1的read系統(tǒng)調(diào)用,然后內(nèi)核幫忙到磁盤中把請求的文件內(nèi)容讀取到kernnel buffer,然后再copy回用戶進(jìn)程空間。

相比,如果用mmap,一開始內(nèi)核就把整個文件映射到了用戶進(jìn)程的虛擬內(nèi)存中;映射過程只是分配了地址空間,并沒有拷貝內(nèi)存,所以速度快。這一段地址空間在代碼層面看到的就是一塊連續(xù)的內(nèi)存,當(dāng)代碼訪問這塊內(nèi)存,如果引發(fā)缺頁異常,內(nèi)核就會加載文件內(nèi)存到buffer。這樣就減少了一次內(nèi)存拷貝。

使用mmap對于大文件的加載和顯示有什么好處呢:

  • 讀取速度快
  • 可以把整個文件當(dāng)做代碼中一個連續(xù)內(nèi)存區(qū)域,直接以const char*訪問,即可以透明地認(rèn)為整個文件已經(jīng)加載到進(jìn)程內(nèi),且保存為一個字符串(指針)了。對于代碼設(shè)計而言較方便。
mmap參考資料: https://www.jianshu.com/p/eece39beee20https://zhuanlan.zhihu.com/p/69555454

Qt里顯示大文件

在Qt里,QFile::map提供了跨平臺的“文件內(nèi)存映射”支持。所以通過調(diào)用QFile::map就可以把文件“加載”為一個const char*字符串使用。

我們知道,在 QPlainTextEdit里,顯示文本一般可以用setPlainText。如果直接把map后的內(nèi)存?zhèn)鬟f給setPlainText會導(dǎo)致文件的所有內(nèi)容被讀入內(nèi)存,這顯然是不行的。

一般對大文件處理方式是“分頁”,也就是一次只加載部分內(nèi)容。

為了讓用戶感知不到文件被“分頁”了,我們需要處理下,自動加載分頁的內(nèi)容。具體的做法:

  • 監(jiān)聽滾動事件,自動加載下一個/上一個分頁
  • 隱藏滾動條,用外部滾動條替代;外部滾動條對應(yīng)整個文件范圍,并保持實(shí)時同步
  • 思路

    在開始實(shí)現(xiàn)前,我們最好有一個清晰的思路,可以建個簡單的模型:

    這里,我們把窗口可視區(qū)想象成一個固定高度的滑塊,整個滑塊可以在整個文件從頭滑動到尾部——對應(yīng)用戶從第一行拉動滾動條(右側(cè)灰色箭頭)直到最后一行。

    為了能減少滾動過程中頻繁觸發(fā)讀取文件,可以設(shè)置一塊預(yù)加載區(qū)域,比可見區(qū)域大。每次可見區(qū)域要滑出預(yù)加載區(qū)的時候,就觸發(fā)一次預(yù)加載區(qū)的預(yù)讀。

    在實(shí)現(xiàn)上,預(yù)加載區(qū)域?qū)?yīng)的就是setPlainText加載的內(nèi)容,而可見區(qū)域的滾動就直接由QPlainText代為實(shí)現(xiàn)了。

    于是,要實(shí)現(xiàn)大文件的加載和顯示,只要: 1. 預(yù)讀內(nèi)容,通過setPlainText到QPlainTextEdit 2. 處理QPlainTextEdit的滾動事件,在即將滾出預(yù)讀區(qū)的時候,更新預(yù)讀區(qū)

    當(dāng)然,說起來容易,做起來還是要處理一些瑣碎事務(wù)的。詳見:https://github.com/compilelife/loginsight/blob/master/src/logtextedit.cpp

    再談文件的內(nèi)存映射

    當(dāng)然,如果只是單純地去顯示一個大文件, 直接用常規(guī)的文件讀寫API也是可行的。map的優(yōu)勢還不夠明顯。

    實(shí)際上,map在這個場景里,真正強(qiáng)大的地方是在于把文件當(dāng)做“已經(jīng)加載好的連續(xù)字符串”。在加載了大文件后,不可避免地需要做查找、定位等邏輯,這時使用map可同時優(yōu)化效率和代碼可讀性。

    比如,我們要在上面工作的基礎(chǔ)上做全文搜索并定位到匹配行。這時QPlainText的find因?yàn)橹荒芩阉黝A(yù)加載內(nèi)容,無法使用。而基于map,只需要對map后的內(nèi)存地址,執(zhí)行strstr按字符串查找,再把查找到的位置前后內(nèi)容載入可視區(qū)即可。

    總結(jié)

    為了基于qt原生控件去高效地顯示大文件,我們用了不少奇技淫巧,把QPlainTextEdit偽裝成了支持大文件的文本框。也許下一步可以試試看用QPlainTextDocumentLayout實(shí)現(xiàn)自定義文本框,作更深入地優(yōu)化。

    總結(jié)

    以上是生活随笔為你收集整理的fread读取整个文件_qt如何实现大文件的加载和显示的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。