golang程序進度條實現示例詳解
引言
最近在工作中寫一個批處理腳本,令人抓狂的是每次都不知道腳本要跑到啥時候結束,于是想到給程序添加個進度條。
逛了一圈,沒找到特別趁手的輪子,本著有手就行的原則,今天簡單地給大家擼一個終端進度條。
原理
終端進度條打印的原理是通過輸入\r將光標位置移動到當前行的行首,重新打印一份進度信息。
如果是使用\n,則光標會另起一行打印信息。
上才藝
首先從核心功能出發(fā),進度條要告訴我的信息有
- 一共要完成多少任務
- 現在完成了多少任務
- 到什么時候才能完成全部任務
根據上面的需求
畫了個大概的樣子長這樣 [█████████████████████████]100/100 [eta]16:33:39
抽象的用戶調用函數有3個
New()新建進度條實例 Done()推進進度條進展 Finish()完成進度條
是不是和sync.WaitGroup很像。
調用代碼
func main() {
bar := progress.New(100)
for i := 0; i < 100; i++ {
time.Sleep(time.Second / 10)
bar.Done(1)
}
bar.Finish()
}
所以根據用戶調用需求,首先定義進度條結構體。
type Bar struct {
total int64 // 總進度
current int64 // 當前進度
filler string // 進度填充字符
filler_length int64 // 進度條長度
time_format string // 進度條時間格式
interval time.Duration // 打印時間間隔
begin time.Time // 任務開始時間
}
然后根據用戶調用的函數,給出函數實現,當然這里面加了一些函數參數可選項。
可以在初始化實例的時候自定義一些元素,比如填充字符,比如時間格式或者是每隔多少時間刷新一次進度條等等。
// New 新建進度條實例
func New(total int64, opts ...func(*Bar)) *Bar {
bar := &Bar{
total: total,
filler: "█",
filler_length: 25,
time_format: "15:04:05", // 2006-01-02T15:04:05
interval: time.Second,
begin: time.Now(),
}
for _, opt := range opts {
opt(bar)
}
// 定時打印
ticker := time.NewTicker(bar.interval)
go func() {
for bar.current < bar.total {
fmt.Print(bar.get_progress_string())// 打印進度
<-ticker.C
}
}()
return bar
}
// Done 更新完成進度
func (bar *Bar) Done(i int64) {
bar.current += i
}
// Finish 完成最后進度條
func (bar *Bar) Finish() {
fmt.Println(bar.get_progress_string())
}
// WithFiller 設置進度條填充字符
func WithFiller(filler string) func(*Bar) {
return func(bar *Bar) {
if len(bar.filler) != 0 {
bar.filler = filler
}
}
}
那么處理完了用戶怎么使用之后,我們就來開始處理怎么給用戶展示進度條效果。
要想根據進度填充不同的字符比例,先算進度百分比,長下面這樣子。
//get_percent 獲取進度百分比,區(qū)間0-100
func (bar *Bar) get_percent() int64 {
return bar.current * 100 / bar.total
}
因為我們進度條并不需要那么精確,所有這里都用的是整數來處理,更方便一些,不用做各種類型轉換。
那么拿到百分比之后,就能根據進度條總長度來計算要填充多少個█。
接下來算任務什么時候完成,這里用的算法是,用當前完成了多少個任務和花了多少時間來估算總任務數的要花費多少時間,得到預計什么時候完成,代碼是這樣子的:
//get_eta 獲取eta時間
func (bar *Bar) get_eta(now time.Time) string {
eta := (now.Unix() - bar.begin.Unix()) * 100 / (bar.get_percent() + 1)
return bar.begin.Add(time.Second * time.Duration(eta)).Format(bar.time_format)
}
最后,我們來處理下需要在控制臺打印的字符串,同時作為非核心需求,我們還想看批處理操作的速度,所以這里用QPS來表達我們整個任務處理的速度。
QPS表達任務處理速度
//get_progress_string 獲取打印控制臺字符串
func (bar *Bar) get_progress_string() string {
fills := bar.get_percent() * bar.filler_length / 100
for i := int64(0); i < bar.filler_length; i++ {
switch {
case i < fills:
chunks[i] = bar.filler
default:
chunks[i] = " "
}
}
now := time.Now()
eta := bar.get_eta(now)
qps := bar.current / (now.Unix() - bar.begin.Unix() + 1)
return fmt.Sprintf("\r[%s]%d/%d [eta]%s [qps]%d ", strings.Join(chunks, ""), bar.current, bar.total, eta, qps)
}
最終呈現的效果 [█████████████████████████]100/100 [eta]16:33:39 [qps]9
當然,為了更酷炫一點,同時還引入了emoji字符,能夠根據字符自適應地調整顯示效果。

下面是項目github地址,供大家參考
https://github.com/jony-lee/go-progress-ba
知識點總結
下面是知識點總結
- 使用\r來將控制臺光標定位到行首實現行內進度條刷新。
- 使用函數可選參數來實現用戶自定義設置。
- 使用函數time.NewTicker()實現定時刷新控制臺進度條。
以上就是golang程序進度條實現示例詳解的詳細內容,更多關于golang程序進度條的資料請關注腳本之家其它相關文章!
相關文章
go 判斷兩個 slice/struct/map 是否相等的實例
這篇文章主要介紹了go 判斷兩個 slice/struct/map 是否相等的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
golang常用庫之操作數據庫的orm框架-gorm基本使用詳解
這篇文章主要介紹了golang常用庫之操作數據庫的orm框架-gorm基本使用,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10

