如何在python中将以前的打印内容覆盖到stdout?


113

如果我有以下代码:

for x in range(10):
     print x

我会得到的输出

1
2
etc..

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



作为一个方面说明,确保你得到的答案,能够冲洗整个行,如果前面的线超过当前行。
水果

Answers:


119

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

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

打印语句末尾的逗号告诉它不要转到下一行。最后的打印语句前进到下一行,因此您的提示不会覆盖您的最终输出。

更新资料

既然Python 2是EOL,Python 3的答案就更有意义了。对于Python 3.5及更早版本:

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

在Python 3.6及更高版本中,f字符串的读取效果更好:

for x in range(10):
    print(f'{x}\r', end="")
print()

这可行,但是我不理解其中的几个部分...'{0}'和.format
ccwhite1 2011年

7
这是Python 2.6中引入的,(有人告诉我)这是Python 3中格式化字符串的标准方法;该%运营商已被弃用。基本上,所有字符串现在都有一个format方法。在这种情况下,其{0}含义是“的第一个参数format()”(计数从0开始)。
Mike DeSimone

如果您必须使用某些仅限Windows的软件并且没有运行虚拟机的印章,那么这不是一个选择。
Mike DeSimone

2
而且,如果您想成为跨终端,re.sub(r'\$<\d+>[/*]?', '', curses.tigetstr('el') or '')则可以为用户使用的任何终端提供正确的行尾擦除字符串。记住要初始化curses喜欢的东西curses.setupterm(fd=sys.stdout.fileno()),并使用sys.stdout.isatty()以确保您的输出没有被重定向到一个文件。有关具有光标控制和颜色支持的完整Python模块,请参见code.activestate.com/recipes/475116
迈克·德西蒙

1
@PhilMacKay:我在Mac上的python中尝试过:在[3]中:打印“要擦除的内容\ r”,time.sleep(1); 打印“ -------------- \ r”,--------------我认为您的问题是Windows及其不同的行尾。尝试以下操作:import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr')));它应该打印字符序列的十六进制代码以转到行的开头。
Mike DeSimone

107

由于我是通过Google结束的,但是正在使用Python 3,因此这是在Python 3中的工作方式:

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

相关答案在这里:如何在打印语句后取消换行符?


3
我尝试了这一点,并且效果很好。唯一的问题是,例如,它不会擦除该行上的先前输出:如果您的第一个循环打印testing和第二个循环打印test在第二遍通过之后的输出仍然是testing-现在,我看到@ Nagasaki45指出了这一点
Eric Uldall

15
在一个ipython笔记本中,我必须做:print("\r", message, end="")
grisaitis

1
这很好用,它不会擦除行,但是在下面的打印中使用足够的空格可以完成这项工作(尽管它不是很优雅)。我可能要补充的一句话是,这在VS Code的调试控制台中不起作用,但在终端中有效。
PhilMacKay

31

@Mike DeSimone的答案可能在大多数时间都有效。但...

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

-> 1bc

这是因为'\r'仅返回到行的开头,但不会清除输出。

编辑:更好的解决方案(比我下面的旧建议)

如果POSIX支持足以满足您的需求,则以下内容将清除当前行并将光标留在其开头:

print '\x1b[2K\r',

它使用ANSI转义码清除端子线。有关更多信息,请参见Wikipedia此精彩演讲

旧答案

我发现的(不太好)解决方案如下所示:

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

-> 1

优点之一是它也可以在Windows上运行。


1
我从“旧答案”中做出了一个可以正常工作的功能(即使在Windows上也是如此),请参见我的答案:stackoverflow.com/a/43952192/965332
erb

25

在访问此线程之前,我有同样的问题。对我来说,sys.stdout.write仅在我正确刷新缓冲区时才起作用,即

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

不冲洗,结果仅在脚本的末尾打印


同样好主意是用空格填充字符串的其余部分,例如stringvar.ljust(10,' ')长度可变的字符串。
Annarfych,

@克里斯,唐等。这是Python 2,大多数其他答案是Python3。现在每个人都应该使用Python 3。Python 2将于2020
寿终正寝。– smci


9

试试这个:

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

它为我工作。该 end="\r"部分正在使其覆盖前一行。

警告!

如果您打印出hi,然后hello使用进行打印\r,您将会得到,hillo因为输出覆盖了前两个字母。如果hi使用空格打印(此处未显示),则将输出hi。要解决此问题,请使用来打印空格\r


1
这对我不起作用。我处理了该代码,但输出为 Hi BobHi BobHi BobHi BobHi Bob。Python 3.7.1
PaulMag,

1
但是Rubixred的答案奏效了。奇怪的。在我看来,它们的方法相同。我看到的唯一区别\r是在开始而不是结束。
PaulMag

7

我无法在此页面上找到任何可用于IPython的解决方案,但对@ Mike-Desimone的解决方案进行了一些改动即可完成工作:与其以回车符结尾而不是以回车符开头

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

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


即使在PyDev控制台中也不起作用,在print()的右括号后面也没有逗号。
Noumenon

不适用于我,它只是在处理完成后写入最终结果(例如:“下载进度为100%”)。
亚历山大

2
@Alexandre在IPython或其后续版本Jupyter中工作正常。您需要刷新缓冲区。stackoverflow.com/questions/17343688/…–
2013

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

time.sleep(0.5)用于显示如何清除先前的输出,并在打印消息开始时“ \ r”打印新的输出,它将在新输出之前擦除先前的输出。



5

公认的答案并不完美。首先打印的行将保留在该行中,如果第二次打印没有覆盖整个新行,则最终将显示垃圾文本。

为了说明问题,请将此代码另存为脚本并运行(或看看):

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%

5

这是@ 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)

5
这是一个很好的解决方案,因为它可以优雅地将以前的输出清理为完整长度,而不必仅打印任意数量的空格(这是我以前所求的!)谢谢
Toby Petty

2

我有点惊讶没有人使用退格字符。这是使用它的一个。

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

确保使用此方法后冲洗,否则可能无法在屏幕上打印任何内容。sys.stdout.flush()
加百利博览会

2

(Python3)这对我有用。如果只使用\ 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

2

根据先前的答案再回答一个。

pbar.py的内容:import 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)

1

这是我的解决方案!Windows 10,Python 3.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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.