Golang如何在stdout上更新一个inplace字符串?

时间:2013-03-15 21:21:41

标签: go stdout

我想输出到stdout并让输出“覆盖”前一个输出。

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

4 个答案:

答案 0 :(得分:75)

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

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

  • 如果stdout要去终端
  • 该终端覆盖线路/屏幕的程序是什么

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

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

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

作为具有不同支持“覆盖”的“其他”终端的示例,以下是playground的示例。

答案 1 :(得分:22)

如果要将多行重写为输出,请使用此解决方案。例如,我使用这种方法做了一个不错的Conway's "Game of Life"输出。

免责声明:这仅适用于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

答案 2 :(得分:3)

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

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

以前的答案不能解决。

答案 3 :(得分:0)

发现了一些值得分享的类似问题。
为将来可能遇到相同问题的人

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

}

Example code taken from