首頁 > 軟體

golang程式進度條實現範例詳解

2022-08-29 14:00:13

引言

最近在工作中寫一個批次處理指令碼,令人抓狂的是每次都不知道指令碼要跑到啥時候結束,於是想到給程式新增個進度條。

逛了一圈,沒找到特別趁手的輪子,本著有手就行的原則,今天簡單地給大家擼一個終端進度條。

原理

終端進度條列印的原理是通過輸入r將遊標位置移動到當前行的行首,重新列印一份進度資訊。

如果是使用n,則遊標會另起一行列印資訊。

上才藝

首先從核心功能出發,進度條要告訴我的資訊有

  • 一共要完成多少任務
  • 現在完成了多少任務
  • 到什麼時候才能完成全部任務

根據上面的需求

畫了個大概的樣子長這樣 [█████████████████████████]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 獲取進度百分比,區間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程式進度條的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com