如何覆盖之前的标准输出打印?

177

假设我有以下代码:

for x in range(10):
     print(x)

我想获取输出结果

1
2
etc..

我想要做的是,不打印换行符,而是在同一行上用新值替换先前的值。


1
可能是重复的问题:Python - 删除和替换已打印的项目 - Sven Marnach
2
顺便提一下,请确保获得的答案能够在当前行比前一行更长时清除整个行。 - 林果皞
19个回答

229

简单版本

一种方法是使用回车符 ('\r') 字符返回到行的开头,而不会前进到下一行。

Python 3

for x in range(10):
    print(x, end='\r')
print()

Python 2.7向前兼容

from __future__ import print_function
for x in range(10):
    print(x, end='\r')
print()

Python 2.7

for x in range(10):
    print '{}\r'.format(x),
print

Python 2.0-2.6

for x in range(10):
    print '{0}\r'.format(x),
print

在后两种情况(仅限Python 2)中,print语句末尾的逗号表示不要换行。最后一个print语句会换行,这样你的提示符就不会覆盖最终输出。
行清理
如果无法保证新的文本行不短于现有的行,则只需添加“清除行末”的转义序列'\x1b[1K' ('\x1b'=ESC)即可。
for x in range(75):
    print('*' * (75 - x), x, end='\x1b[1K\r')
print()

8
这是Python 2.6中引入的,并且(据说)是Python 3中格式化字符串的标准方式;%运算符已不推荐使用。基本上,现在所有字符串都有一个format方法。 在这种情况下,{0}表示“format()的第一个参数”(计数从0开始)。 - Mike DeSimone
3
如果你想在不同的终端上运行,可以使用 re.sub(r'\$<\d+>[/*]?', '', curses.tigetstr('el') or '') 命令来获取用户所在终端的正确清除该行剩余部分的字符串。请记得使用类似 curses.setupterm(fd=sys.stdout.fileno()) 的命令初始化 curses ,并使用 sys.stdout.isatty() 判断输出是否被重定向到文件中。查看 http://code.activestate.com/recipes/475116/ 获取包含光标控制和颜色支持的完整 Python 模块。 - Mike DeSimone
1

@PhilMacKay:我在我的Mac上尝试了Python: In [3]: print "Thing to erase\r", ; time.sleep(1) ; print "--------------\r",

我认为你的问题是Windows和它不同的行结尾。尝试这个:import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr'))); 它应该打印字符序列的十六进制代码,以便回到行的开头。
- Mike DeSimone
1
我认为在Python 3中不需要格式。end='\r' 不也可以吗? - Tim Seguine
1
如果新行比前一行短,则前一行的尾部将保持不变。 - Elliott B
显示剩余8条评论

112

4
我尝试了这个方法,大部分情况下都可以正常工作。唯一的问题是它没有清除该行之前的输出,例如:如果你的第一个循环打印了“testing”,而你的第二个循环打印了“test”,那么经过第二次循环后的输出仍将是“testing” - 现在我看到@Nagasaki45指出了这一点。 - Eric Uldall
20
在一个IPython Notebook中,我需要执行以下操作:print("\r", message, end="") - william_grisaitis
2
这个很好用,它不会删除该行,但在下一个打印中使用足够的空格就可以完成任务(虽然不太优雅)。 我想补充一点的是,在VS Code的调试控制台中无法使用此方法,但在终端中可以。 - PhilMacKay
@EricUldall 和 @PhilMacKay,诀窍是在消息后面打印一些空格。例如: print("Progress {:2.1%} ".format(x), end="\r")因此,当 x 的长度从 3 位数变为 2 位数时,尾随的空格将擦除多余的数字。 - Ben L

35

@Mike DeSimone的回答大部分时间都是有效的。但是...

for x in ['abc', 1]:
    print '{}\r'.format(x),

-> 1bc

这是因为 '\r' 只会回到一行的开头,但不会清除输出内容。

如果您只需要 POSIX 支持,以下方法可以清除当前行并将光标保留在其开头:

print '\x1b[2K\r',

它使用ANSI转义码来清除终端行。可以在维基百科这个优秀的演讲中找到更多信息。

其他方法

我发现的另一种(可以说更糟糕)解决方案如下:

last_x = ''
for x in ['abc', 1]:
    print ' ' * len(str(last_x)) + '\r',
    print '{}\r'.format(x),
    last_x = x

-> 1

其中一个优点是它也可以在Windows上运行。


1
我将“旧答案”制作成一个函数,它可以正常工作(即使在Windows上),请参见我的答案:https://dev59.com/5G035IYBdhLWcg3wcviS#43952192 - erb

29

在查看此线程之前,我也有同样的疑问。对我而言,只有在正确刷新缓冲区后,sys.stdout.write才有效。

for x in range(10):
    sys.stdout.write('\r'+str(x))
    sys.stdout.flush()

没有刷新,结果仅在脚本结束时输出。


对于长度可变的字符串,像 stringvar.ljust(10,' ') 这样用空格填充字符串的其余部分也是一个好主意。 - Annarfych
@chris,Don等人,这是Python 2,大多数其他答案都是Python 3。现在每个人都应该使用Python 3;Python 2将于2020年停止更新。 - smci

18

压制换行并打印 \r

print 1,
print '\r2'

或者将其写入标准输出:

sys.stdout.write('1')
sys.stdout.write('\r2')

11
这是@Nagasaki45答案的一个更简洁、更易操作的版本,与其他很多答案不同的是,它可以正确处理不同长度的字符串。它通过打印与上一行相同长度的空格来清除该行内容,从而实现这一点。同时也可以在Windows系统中使用。
def print_statusline(msg: str):
    last_msg_length = len(getattr(print_statusline, 'last_msg', ''))
    print(' ' * last_msg_length, end='\r')
    print(msg, end='\r')
    sys.stdout.flush()  # Some say they needed this, I didn't.
    setattr(print_statusline, 'last_msg', msg)

使用方法

只需按照以下方式使用:

for msg in ["Initializing...", "Initialization successful!"]:
    print_statusline(msg)
    time.sleep(1)

这个小测试展示了即使长度不同,行也能被正确地清除:
for i in range(9, 0, -1):
    print_statusline("{}".format(i) * i)
    time.sleep(0.5)

5
这是一个很好的解决方案,因为它能够优雅地将之前的输出完全清除,而不仅仅是打印出任意数量的空格(这是我之前采用的方法!)谢谢。 - Toby Petty
我以前从未见过将属性附加到函数上。不错的技巧。 - niid

11
for x in range(10):
    time.sleep(0.5) # shows how its working
    print("\r {}".format(x), end="")

time.sleep(0.5) 是为了展示先前输出被擦除并打印新输出时使用的。 "\r"当它在打印消息的开头时,它将在新输出之前擦除先前的输出。


9

试试这个:

import time
while True:
    print("Hi ", end="\r")
    time.sleep(1)
    print("Bob", end="\r")
    time.sleep(1)

对我而言它是有效的。 end="\r"这个部分会使它覆盖在先前的一行上。

警告!

如果您打印hi,然后使用\r打印hello,您将得到hillo,因为输出写在了前两个字母之上。如果您用空格打印hi(这里不显示),那么它将输出hi。要解决这个问题,请使用\r打印出空格。


1
这对我不起作用。我复制了那段代码,但我的输出是Hi BobHi BobHi BobHi BobHi Bob。Python 3.7.1 - PaulMag
1
但是Rubixred的答案有效。奇怪。在我看来,它们看起来像是同样的方法。我唯一看到的区别是\r在开头而不是结尾。 - PaulMag
这是迄今为止唯一有效的一个。 - undefined

8

这适用于Windows和Python 3.6。

import time
for x in range(10):
    time.sleep(0.5)
    print(str(x)+'\r',end='')

7

我尝试了这个页面上的所有解决方案都无法用于IPython,但是对@Mike-Desimone的解决方案做了一点微调就可以了:不要以回车符结束行,而是以回车符开头

for x in range(10):
    print '\r{0}'.format(x),

此外,这种方法不需要第二个打印语句。

即使在PyDev控制台中也无法工作,print()的右括号后面加逗号也不行。 - Noumenon
对我来说没有用,它只是在进程完成后写入最终结果(例如:“下载进度100%”)。 - Alexandre
2
@Alexandre 在IPython或其继任者Jupyter中它可以正常工作。您需要清除缓冲区。https://dev59.com/TnPYa4cB1Zd3GeqPntE6 - March Ho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接