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

如果我有以下代码:

for x in range(10):
print x

我会得到

1
2
etc..

我想要做的不是打印一个换行符,而是替换以前的值,并用同一行上的新值覆盖它。

236928 次浏览

取消换行并打印 \r

print 1,
print '\r2'

或者写信给 stdout:

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

简单版

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

巨蟒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

在后两种情况下(仅针对 Python2) ,print 语句末尾的逗号告诉它不要转到下一行。最后一个 print 语句将前进到下一行,因此提示符不会覆盖最终输出。

线路清理

如果您不能保证新的文本行不短于现有的行,那么您只需添加一个“ clear to end of line”转义序列 '\x1b[1K'('\x1b' = ESC) :

for x in range(75):
print('*' * (75 - x), x, end='\x1b[1K\r')
print()

因为我是通过 Google 来到这里的,但是我使用的是 Python 3,下面是 Python 3的工作原理:

for x in range(10):
print("Progress {:2.1%}".format(x / 10), end="\r")

相关答案: 如何在 print 语句后禁止显示换行符?

@ 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

一个优点是它也可以在窗口上工作。

我在这个页面上找不到适合 IPython的任何解决方案,但是@Mike-Desimone 的解决方案的一个小小的变化就起到了作用: 不是用回车结束行,而是用回车结束行:

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

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

在访问这个帖子之前,我也有同样的问题。对我来说,sys.stdout.write 只有在正确刷新缓冲区(即。

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

如果不进行刷新,结果只会在脚本的末尾打印出来

试试这个:

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打印空格。

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

Sleep (0.5)用于显示如何擦除以前的输出并打印新的输出 当“ r”位于打印消息的开头时,它会在新输出之前删除以前的输出。

这里有一个更干净,更“即插即用”的版本@Nagasaki45的答案。与这里的许多其他答案不同,它适用于不同长度的字符串。它通过使用与最后一行打印的长度一样多的空格清除该行来实现这一点。也可以在 Windows 上使用。

def print_statusline(msg: str):
last_msg_length = len(print_statusline.last_msg) if hasattr(print_statusline, 'last_msg') else 0
print(' ' * last_msg_length, end='\r')
print(msg, end='\r')
sys.stdout.flush()  # Some say they needed this, I didn't.
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)

我有点惊讶没有人使用退格字符。

import sys
import time


secs = 1000


while True:
time.sleep(1)  #wait for a full second to pass before assigning a second
secs += 1  #acknowledge a second has passed


sys.stdout.write(str(secs))


for i in range(len(str(secs))):
sys.stdout.write('\b')

公认的答案不是 太好了。第一次打印的行将保留在那里,如果第二次打印没有覆盖整个新行,则最终将得到垃圾文本。

为了说明这个问题,将这段代码保存为一个脚本并运行它(或者只是查看一下) :

import time


n = 100
for i in range(100):
for j in range(100):
print("Progress {:2.1%}".format(j / 100), end="\r")
time.sleep(0.01)
print("Progress {:2.1%}".format(i / 100))

输出如下:

Progress 0.0%%
Progress 1.0%%
Progress 2.0%%
Progress 3.0%%

对我来说,在留下永久性的指纹之前清除这条线是有效的。你可以根据自己的具体问题随意调整:

import time


ERASE_LINE = '\x1b[2K' # erase line command
n = 100
for i in range(100):
for j in range(100):
print("Progress {:2.1%}".format(j / 100), end="\r")
time.sleep(0.01)
print(ERASE_LINE + "Progress {:2.1%}".format(i / 100)) # clear the line first

现在它正如预期的那样打印出来:

Progress 0.0%
Progress 1.0%
Progress 2.0%
Progress 3.0%

这可以在 Windows 和 python 3.6上运行

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

这是我的解决方案! Windows10,Python3.7.1

我不知道这段代码为什么有效,但它完全删除了原来的行。我根据之前的答案编辑的。其他的答案只是把线返回到开始,但是如果你之后有一个较短的线,它会看起来像 hello变成 byelo一样混乱。

import sys
#include ctypes if you're on Windows
import ctypes
kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
#end ctypes


def clearline(msg):
CURSOR_UP_ONE = '\033[K'
ERASE_LINE = '\x1b[2K'
sys.stdout.write(CURSOR_UP_ONE)
sys.stdout.write(ERASE_LINE+'\r')
print(msg, end='\r')


#example
ig_usernames = ['beyonce','selenagomez']
for name in ig_usernames:
clearline("SCRAPING COMPLETE: "+ name)

输出-每一行将被重写,没有任何旧的文本显示:

SCRAPING COMPLETE: selenagomez

下一行(完全重写在同一行) :

SCRAPING COMPLETE: beyonce

(Python 3)这就是我的工作。如果你只是使用010,那么它会留下字符,所以我稍微调整了一下,以确保它覆盖了那里有什么。这还允许您在第一个打印项之前拥有某些内容,并且只删除该项的长度。

print("Here are some strings: ", end="")
items = ["abcd", "abcdef", "defqrs", "lmnop", "xyz"]
for item in items:
print(item, end="")
for i in range(len(item)): # only moving back the length of the item
print("\010 \010", end="") # the trick!
time.sleep(0.2) # so you can see what it's doing

根据前面的答案再回答一个问题。

Pbar.py 的内容: 导入 sys,shutil,datetime

last_line_is_progress_bar=False




def print2(print_string):
global last_line_is_progress_bar
if last_line_is_progress_bar:
_delete_last_line()
last_line_is_progress_bar=False
print(print_string)




def _delete_last_line():
sys.stdout.write('\b\b\r')
sys.stdout.write(' '*shutil.get_terminal_size((80, 20)).columns)
sys.stdout.write('\b\r')
sys.stdout.flush()




def update_progress_bar(current, total):
global last_line_is_progress_bar
last_line_is_progress_bar=True


completed_percentage = round(current / (total / 100))
current_time=datetime.datetime.now().strftime('%m/%d/%Y-%H:%M:%S')
overhead_length = len(current_time+str(current))+13
console_width = shutil.get_terminal_size((80, 20)).columns - overhead_length
completed_width = round(console_width * completed_percentage / 100)
not_completed_width = console_width - completed_width
sys.stdout.write('\b\b\r')


sys.stdout.write('{}> [{}{}] {} - {}% '.format(current_time, '#'*completed_width, '-'*not_completed_width, current,
completed_percentage),)
sys.stdout.flush()

剧本用法:

import time
from pbar import update_progress_bar, print2




update_progress_bar(45,200)
time.sleep(1)


update_progress_bar(70,200)
time.sleep(1)


update_progress_bar(100,200)
time.sleep(1)




update_progress_bar(130,200)
time.sleep(1)


print2('some text that will re-place current progress bar')
time.sleep(1)


update_progress_bar(111,200)
time.sleep(1)


print('\n') # without \n next line will be attached to the end of the progress bar
print('built in print function that will push progress bar one line up')
time.sleep(1)


update_progress_bar(111,200)
time.sleep(1)

最好覆盖整行,否则如果新行更短,新行将与旧行混合。

import time, os
for s in ['overwrite!', 'the!', 'whole!', 'line!']:
print(s.ljust(os.get_terminal_size().columns - 1), end="\r")
time.sleep(1)

在 Windows 上必须使用 columns - 1

这对我很有用,在 Spyder 中使用 Python 3.7.9,在 Windows 中:

from IPython.display import clear_output
from time import sleep


def print_and_overwrite(text):
'''Remember to add print() after the last print that you want to overwrite.'''
clear_output(wait=True)
print(text, end='\r')


for i in range(15):
#I print the results backwards (from 15 to 1), to test shorter strings
message = "Iteration %d out of 15" %(15-i)
print_and_overwrite(message)
sleep(0.5)


print() #This stops the overwriting
print("This will be on a new line")

无论如何,如果有人想套印(清除)一个 很多 台词以前打印在标准输出,那么这个答案应该对他有帮助。(感谢 Thijmen 大坝的文章 覆盖以前打印的行)

在 ANSI 控制台中,您可以使用特殊的序列:

\033[1A\033[K

首先他们提起一个光标,其次完全擦除一行。

清除控制台的示例(Python3) :

LINE_UP = '\033[1A'
LINE_CLEAR = '\033[K'
CONSOLE_HEIGHT = 24 #lines


def clear_console():
for a in range(CONSOLE_HEIGHT):
print(LINE_UP, end=LINE_CLEAR, flush=True)

或最终简单(将清除屏幕并将光标移动到0,0) :

print('\033[2J', end='', flush=True)

如果只想定位光标,那么使用以下方法:

print('\033[<L>;<C>f', end='', flush=True)

其中 <L><C>是相应的直线和列。

为您提供少量参考 ANSI 转义序列