實戰(zhàn)項目:用 Go 實現(xiàn)進(jìn)度條功能
最近在做一個需求,功能很簡單,就是開發(fā)一個輕量級客戶端,將一個指定文件中的內(nèi)容通過TCP發(fā)送到服務(wù)器。由于該文件特別大,有可能到達(dá) 100G 的數(shù)量級,因此處理起來會比較慢,為了給用戶提供比較友好的展示界面,因此,在其中加入了進(jìn)度條顯示功能。
在這里,說一下我在實現(xiàn)該進(jìn)度條功能時的一些思路。
成果演示
先看一下最終的成品效果展示:
該進(jìn)度條一共分三部分組成,第一部分是主體進(jìn)度條,第二部分是百分比,第三部分是當(dāng)前完成的數(shù)據(jù)和總數(shù)據(jù)的一個動態(tài)展示。
源碼分析
由于是要在終端上打印出進(jìn)度條的效果,因此,主要還是利用fmt.Printf函數(shù)中的\r格式控制符。有了這個基礎(chǔ),我們就可以先設(shè)計一下結(jié)構(gòu),如下所示:
type?Bar?struct?{
????percent?int64??//百分比
????cur?????int64??//當(dāng)前進(jìn)度位置
????total???int64??//總進(jìn)度
????rate????string?//進(jìn)度條
????graph???string?//顯示符號
}
其中,百分比沒什么說的,cur和total是一組,表示的就是第三部分動態(tài)展示的當(dāng)前完成數(shù)據(jù)和總數(shù)據(jù)。rate就是第一部分不斷變化的進(jìn)度條,它是一個string類型的字符串。這個進(jìn)度條顯示工具還提供了一個叫graph的屬性,有了它,用戶就可以自定義進(jìn)度條顯示的圖案,比如可以把進(jìn)度條中的方塊換成#、=、@等你可以想得到的圖案。
初始化
為了能夠方便的調(diào)用該進(jìn)度條工具,因此,為該結(jié)構(gòu)提供了兩個初始化的方法,分別為NewOption和NewOptionWithGraph,第二個初始化的方法即可以自己指定顯示圖案。
NewOption使用的是默認(rèn)的顯示圖案,也就是上圖展示的方框。其實現(xiàn)代碼如下所示:
func?(bar?*Bar)?NewOption(start,?total?int64)?{
????bar.cur?=?start
????bar.total?=?total
????if?bar.graph?==?""?{
????????bar.graph?=?"█"
????}
????bar.percent?=?bar.getPercent()
????for?i?:=?0;?i?int(bar.percent);?i?+=?2?{
????????bar.rate?+=?bar.graph?//初始化進(jìn)度條位置
????}
}
該函數(shù)提供了兩個參數(shù),分別為start和total,total不用說,它代表的是總的任務(wù)量,還提供了一個start參數(shù),說明可以不從0開始,這也就意味著, 如果你的程序要支持?jǐn)帱c續(xù)傳功能,這個進(jìn)度條工具依然可以完美支持,只需要將start值設(shè)置在斷點處即可。當(dāng)然了,如果你不需要斷點續(xù)傳,每次都從0開始,只需要將start值設(shè)置為 0 即可。如果你注意到我在初始化進(jìn)度條位置的時候,我使用了i += 2的步長,這就是我接下來要說的。因為百分比總是從0到100,而我的進(jìn)度條長度最長為 50 個字符,這也就意味著,每增長2%,進(jìn)度條就要漲一格,因此,這里的步長為 2。getPercent是一個根據(jù)cur和total獲取當(dāng)前進(jìn)度完成百分比的一個函數(shù),其實現(xiàn)比較簡單:
func?(bar?*Bar)?getPercent()?int64?{
????return?int64(float32(bar.cur)?/?float32(bar.total)?*?100)
}
第二個初始化函數(shù)就比較容易實現(xiàn)了,只需要把graph重新覆蓋之后,直接調(diào)用上面的初始化函數(shù)即可。
func?(bar?*Bar)?NewOptionWithGraph(start,?total?int64,?graph?string)?{
????bar.graph?=?graph
????bar.NewOption(start,?total)
}
進(jìn)度條展示
那么,如何實現(xiàn)顯示功能呢?一般調(diào)用顯示進(jìn)度條時,都是放在循環(huán)中執(zhí)行的,因此,我們只需要在循環(huán)中能夠展示出每輪循環(huán)當(dāng)前的進(jìn)度狀態(tài)即可。
func?(bar?*Bar)?Play(cur?int64)?{
????bar.cur?=?cur
????last?:=?bar.percent
????bar.percent?=?bar.getPercent()
????if?bar.percent?!=?last?&&?bar.percent%2?==?0?{
????????bar.rate?+=?bar.graph
????}
????fmt.Printf("\r[%-50s]%3d%%??%8d/%d",?bar.rate,?bar.percent,?bar.cur,?bar.total)
}
這段代碼中,最重要的就是最后的使用fmt.Printf打印的那一句,通過\r控制打印效果。
當(dāng)然了,在構(gòu)建rate進(jìn)度條時,我需要保存上一次完成的百分比,只有當(dāng)百分比發(fā)生了變化,且步長變化了2時,才需要改變進(jìn)度條的長度。如果你的屏幕足夠大,你也可以讓你的進(jìn)度條長度為100個字符,這樣,你就不需要控制進(jìn)度條的步長為 2 了,每增長1%,進(jìn)度條前進(jìn) 1 格,也是沒有問題的。
結(jié)束
由于上面的打印沒有打印換行符,因此,在進(jìn)度全部結(jié)束之后(也就是跳出循環(huán)之外時),需要打印一個換行符,因此,封裝了一個Finish函數(shù),該函數(shù)純粹的打印一個換行,表示進(jìn)度條已經(jīng)完成。
func?(bar?*Bar)?Finish(){
????fmt.Println()
}
如何調(diào)用
調(diào)用該進(jìn)度條功能,首先,肯定要構(gòu)建一個Bar對象,使用該對象進(jìn)行初始化后,即可完成進(jìn)度條的調(diào)用了,一個完整的調(diào)用程序如下所示:
func?main(){
????var?bar?progressbar.Bar
????bar.NewOption(0,?100)
????for?i:=?0;?i<=100;?i++{
????????time.Sleep(100*time.Millisecond)
????????bar.Play(int64(i))
????}
????bar.Finish()
}
以上是一個最簡單的調(diào)用,其運(yùn)行效果如下所示:
當(dāng)然了,你也可以使用另一個初始化函數(shù)指定顯示的圖標(biāo),如下所示:
bar.NewOptionWithGraph(0,?100,?"#")
展示效果則如下所示:
當(dāng)然,實際使用中,你太可能只利用睡眠,而是需要實現(xiàn)自己的函數(shù)功能,只需要將time.Sleep(100*time.Millisecond)換成自己的代碼邏輯即可。
作者:禹鼎侯
鏈接:https://segmentfault.com/a/1190000023375330
推薦閱讀
站長 polarisxu
自己的原創(chuàng)文章
不限于 Go 技術(shù)
職場和創(chuàng)業(yè)經(jīng)驗
Go語言中文網(wǎng)
每天為你
分享 Go 知識
Go愛好者值得關(guān)注
