如何使用在stdout上更新的原地字符串

huangapple go评论78阅读模式
英文:

How to have an in-place string that updates on stdout

问题

我想要将输出输出到标准输出,并且让输出“覆盖”之前的输出。

例如:如果我输出 On 1/10,我希望下一个输出 On 2/10 覆盖 On 1/10。我该如何做到这一点?

英文:

I want to output to stdout and have the output "overwrite" the previous output.

For example; if I output On 1/10, I want the next output On 2/10 to overwrite On 1/10. How can I do this?

答案1

得分: 124

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

因此,正确的方法是首先检查:

  • stdout是否要输出到终端
  • 终端上覆盖一行/屏幕的过程是什么

上述两点超出了本问题的范围,但让我们假设终端是我们的设备。通常,打印:

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

将覆盖终端上的上一行。\r代表回车,由许多终端实现为将光标移动到当前行的开头,从而提供“覆盖行”的功能。

作为“其他”终端具有不同支持的“覆盖”功能的示例,请参见playground上的示例。

英文:

stdout is a stream (io.Writer). You cannot modify what was already written to it. What can be changed is how that stream's represented in case it is printed to a terminal. Note that there's no good reason to assume this scenario. For example, a user could redirect stdout to a pipe or to a file at will.

So the proper approach is to first check:

  • if the stdout is going to a terminal
  • what is that terminal's procedure to overwrite a line/screen

Both of the above are out of this question's scope, but let's assume that a terminal is our device. Then usually, printing:

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

will overwrite the previous line in the terminal. \r stands for carriage return, implemented by many terminals as moving the cursor to the beginning of the current line, hence providing the "overwrite line" facility.

As an example of "other" terminal with a differently supported 'overwriting', here is an example at the playground.

答案2

得分: 36

使用此解决方案,如果您想将多行重写到输出中。例如,我使用这种方法制作了一个不错的康威的“生命游戏”输出。

免责声明:这仅适用于ANSI终端,并且除了使用fmt之外,这也不是一个特定于Go的答案。

fmt.Printf("3[0;0H")
// 在这里放入其他的 fmt.Printf(...) 

简要说明:这是一个转义序列,告诉ANSI终端将光标移动到屏幕上的特定位置。\033[是所谓的转义序列,0;0H是告诉终端将光标移动到终端的第0行,第0列的代码类型。

来源:https://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements

英文:

Use this solution if you want to rewrite multiple lines to the output. For instance, I made a decent Conway's "Game of Life" output using this method.

DISCLAIMER: this only works on ANSI Terminals, and besides using fmt this isn't a Go-specific answer either.

fmt.Printf("3[0;0H")
// put your other fmt.Printf(...) here

Brief Explanation: this is an escape sequence which tells the ANSI terminal to move the cursor to a particular spot on the screen. The \033[ is the so-called escape sequence, and the 0;0H is the type of code telling the terminal move the cursor to row 0, column 0 of the terminal.

Source: https://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements

答案3

得分: 15

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

fmt.Printf("\033[2K\r%d", i)

例如,它可以正确地从10打印到0:

for i:= 10; i>=0; i-- {
	fmt.Printf("3[2K\r%d", i)
	time.Sleep(1 * time.Second)
}
fmt.Println()

之前的答案无法解决这个问题。

英文:

The solution for one string which will replace whole string

fmt.Printf("\033[2K\r%d", i)

For example, it correctly prints from 10 to 0:

for i:= 10; i>=0; i-- {
	fmt.Printf("3[2K\r%d", i)
	time.Sleep(1 * time.Second)
}
fmt.Println()

which previous answers don't solve.

答案4

得分: 4

发现了一些值得分享的东西,适用于这类问题。
<br>分享给可能在未来面临相同问题的人们

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

}

示例代码来自

英文:

Found something worth sharing for problems like this.
<br>Sharing for people who might be facing same problem in future

Check if output is being written to terminal. If so, use \r (carriage return) defined by terminal to move cursor to the beginning of line

package main

import (
	&quot;flag&quot;
	&quot;fmt&quot;
	&quot;os&quot;
	&quot;time&quot;
)

var spinChars = `|/-\`

type Spinner struct {
	message string
	i       int
}

func NewSpinner(message string) *Spinner {
	return &amp;Spinner{message: message}
}

func (s *Spinner) Tick() {
	fmt.Printf(&quot;%s %c \r&quot;, 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()&amp;os.ModeCharDevice != 0
}

func main() {
	flag.Parse()
	s := NewSpinner(&quot;working...&quot;)
    isTTY := isTTY()
	for i := 0; i &lt; 100; i++ {
		if isTTY {
			s.Tick()
		}
		time.Sleep(100 * time.Millisecond)
	}

}

Example code taken from

huangapple
  • 本文由 发表于 2013年3月16日 05:21:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/15442292.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定