我想输出到标准输出并让输出“覆盖”以前的输出。
例如; 如果我输出On 1/10
,我希望下一个输出On 2/10
覆盖On 1/10
。我怎样才能做到这一点?
stdout
是一个流 ( io.Writer
)。您无法修改已写入的内容。可以更改的是该流在打印到终端时的表示方式。请注意,没有充分的理由假设这种情况。例如,用户可以随意将标准输出重定向到管道或文件。
所以正确的方法是首先检查:
以上两个都超出了这个问题的范围,但是让我们假设终端是我们的设备。然后通常打印:
fmt.Printf("\rOn %d/10", i)
将覆盖终端中的上一行。\r
代表carriage return
,由许多终端实现为将光标移动到当前行的开头,因此提供了“覆盖行”功能。
作为具有不同支持“覆盖”的“其他”终端的示例,这里是操场上的示例。
如果您想将多行重写为输出,请使用此解决方案。例如,我使用这种方法制作了一个不错的Conway 的“生命游戏”输出。
免责声明:这仅适用于 ANSI 终端,除了使用fmt
它也不是特定于 Go 的答案。
fmt.Printf("\033[0;0H")
// put your other fmt.Printf(...) here
简要说明:这是一个转义序列,它告诉 ANSI 终端将光标移动到屏幕上的特定位置。\033[
是所谓的转义序列,是0;0H
告诉终端将光标移动到终端的第 0 行第 0 列的代码类型。
来源:https ://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
一个字符串的解决方案将替换整个字符串
fmt.Printf("\033[2K\r%d", i)
例如,它正确地从 10 打印到 0:
for i:= 10; i>=0; i-- {
fmt.Printf("\033[2K\r%d", i)
time.Sleep(1 * time.Second)
}
fmt.Println()
以前的答案没有解决。
找到了一些值得分享的问题。
分享给未来可能面临同样问题的人
检查输出是否正在写入终端。如果是这样,使用终端定义的 \r (回车)将光标移动到行首
package main
import (
"flag"
"fmt"
"os"
"time"
)
var spinChars = `|/-\`
type Spinner struct {
message string
i int
}
func NewSpinner(message string) *Spinner {
return &Spinner{message: message}
}
func (s *Spinner) Tick() {
fmt.Printf("%s %c \r", s.message, spinChars[s.i])
s.i = (s.i + 1) % len(spinChars)
}
func isTTY() bool {
fi, err := os.Stdout.Stat()
if err != nil {
return false
}
return fi.Mode()&os.ModeCharDevice != 0
}
func main() {
flag.Parse()
s := NewSpinner("working...")
isTTY := isTTY()
for i := 0; i < 100; i++ {
if isTTY {
s.Tick()
}
time.Sleep(100 * time.Millisecond)
}
}