66

我想输出到标准输出并让输出“覆盖”以前的输出。

例如; 如果我输出On 1/10,我希望下一个输出On 2/10覆盖On 1/10。我怎样才能做到这一点?

4

4 回答 4

114

stdout是一个流 ( io.Writer)。您无法修改已写入的内容。可以更改的是该流在打印到终端时的表示方式。请注意,没有充分的理由假设这种情况。例如,用户可以随意将标准输出重定向到管道或文件。

所以正确的方法是首先检查:

  • 如果标准输出要到终端
  • 该终端覆盖行/屏幕的程序是什么

以上两个都超出了这个问题的范围,但是让我们假设终端是我们的设备。然后通常打印:

fmt.Printf("\rOn %d/10", i)

将覆盖终端中的上一行。\r代表carriage return,由许多终端实现为将光标移动到当前行的开头,因此提供了“覆盖行”功能。

作为具有不同支持“覆盖”的“其他”终端的示例,这里是操场上的示例。

于 2013-03-15T21:54:08.550 回答
33

如果您想将多行重写为输出,请使用此解决方案。例如,我使用这种方法制作了一个不错的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

于 2015-11-03T21:57:24.837 回答
12

一个字符串的解决方案将替换整个字符串

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()

以前的答案没有解决。

于 2018-09-17T12:01:50.557 回答
2

找到了一些值得分享的问题。
分享给未来可能面临同样问题的人

检查输出是否正在写入终端。如果是这样,使用终端定义的 \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)
    }

}

示例代码取自

于 2019-12-06T05:14:56.967 回答