进度条实现

进度条一般出现在下载时.如果在下载文件时能够出现一个进度条,知道下载的进度的话,那就很人性化了.那么,进度条到底是怎么实现的呢?

显示进度

最开始,我们来实现一个基础的进度功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"time"
"fmt"
)

func main() {
process(10)
}

func process(sum int){
for i:=0;i<sum;i++{
fmt.Printf("(%d/%d)\r",i,sum)
time.Sleep(500*time.Millisecond)
}
}

运行:

进度

其中\r字符可以将光标移至一行的行首,新打印的字符串将覆盖原有的字符串,从而实现原地输出的效果

但是这样的话有一个潜在的问题:\r只能将光标移动至行首,却不具备清空一行的能力.如果新打印的字符串的长度小于原来字符串的话,那么剩余的字符将不能被覆盖.因此我们还需要在每次打印进度条之前多做一个步骤:清空一整行

1
fmt.Printf("%*c\r",100,' ')

其中%*c指定打印的宽度.它接受2个参数,第1个参数表示打印空格的个数;第2个参数表示打印的字符.这里的作用是清空一行

进度条底部输出

往往程序输出的不会只有进度条,还有其他的一些输出信息,因此我们需要将进度条固定在底部:

1
2
3
4
5
6
7
8
9
func process(count int) {
for i := 0; i < count; i++ {
fmt.Printf("\r%*c\r", 20+i, ' ')
fmt.Println(i)
fmt.Printf("(%d/%d): %s", i+1, count, strings.Repeat("#", i))
time.Sleep(500 * time.Millisecond)
}
println()
}

运行:

底部进度条

其中fmt.Println(i)是需要输出的内容,设置间隔0.5毫秒输出一次

实现进度条类

以下是我实现的简易进度条类:

processbar.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package processbar

import (
"errors"
"fmt"
"strings"
)

type ProcessBar struct {
count int
total int
}

func New(total int) ProcessBar {
return ProcessBar{total: total}
}

func (this ProcessBar) Print() {
percent := this.count * 100 / this.total
fmt.Printf("(%3d%%): |%-100s|", percent, strings.Repeat("#", percent))
}

func (this *ProcessBar) Move(count int) error {
if count >= 0 && count <= this.total {
this.count = count
return nil
}
return errors.New("out of the range!")
}

func (this *ProcessBar) Log(s interface{}) error {
if this.count == this.total {
return errors.New("out of the range!")
}

this.count++
fmt.Printf("\r%*c\r", 150, ' ')
fmt.Println(s)
this.Print()

return nil
}

main.go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"hello/processbar"
"time"
)

func main() {
total := 30
bar := processbar.New(total)

for i := 0; i < total; i++ {
time.Sleep(200 * time.Millisecond)
bar.Log(fmt.Sprintf("logging: %d", i))
//bar.Log(i)
}
}

运行:

进度条类

这样就可以方便的查看程序运行的进度了,只需要使用Log()方法就可以很方便的记录输出日志,并同时打印底部进度条

0%