实现控制台进度条的核心在于\r回车控制符,\r可以让一行的输出回到一行的开头。

以下是一个简单的示例

package main

import (
    "fmt"
    "time"
)

func main() {
    for i := 0; i < 10; i++ {
        fmt.Printf("\r%d", i)
        time.Sleep(time. Second)
    }
}

运行上述代码,就可以看到在同一位置,每隔一秒后输出从0到9的递增数字。注意必须使用fmt.Printf而不是log.Printf,也就是必须使用程序所提供的没有带任何封装的内置打印函数。

位置控制

以上的示例仅仅只是描述了如何使得进度条在同一行上打印内容,而不是换行输出,如果是需要实现进度条和关键信息位置固定,那么就需要使用到格式化输出的占位控制。

如果要实现[########                 ]进度条。假设需要实现输出100个#,那么[]之间就需要预留出100个#的位置,那么就是%100s。当左侧的#不够,右侧需要用空格补齐,那么就要在100前面添加-号,最后就是%-100s。如果是需要左侧补齐,那么就是%100-s。

在进度条后面实现数字进度标识,0%-100%,数字的最大长度为3位,那么也就是%3d。后面的%使用%%转义,最后也就是%3d%%。

针对下载/处理文件的操作,如需要在进度条的最后,标识已下载/处理的文件和文件总大小的信息。那么也就是%d/%d。在评估文件的大小以后,再确定占位的大小,加入文件的大小数字不超过12位,那么就是%12d/%12d。其他情况同理。

以下是一个简单的示例

package main

import (
    "fmt"
    "strings"
    "time"
)

func main() {
    l := 100000
    for i := 0; i < l; i++ {
        rate := int((float64(i) / float64(l)) * 100)
        fmt.Printf("\r[%-100s] %3d%% %6d/%6d", strings.Repeat("#", rate), rate, i, l)
        time.Sleep(time.Millisecond)
    }
}