如何将先前的打印覆盖到标准输出?
- 2024-12-06 08:40:00
- admin 原创
- 87
问题描述:
如果我有以下代码:
for x in range(10):
print(x)
我会得到输出
1
2
etc..
我想要做的是,不是打印换行符,而是替换先前的值并用同一行上的新值覆盖它。
解决方案 1:
简易版
一种方法是使用回车`'
'`符 ( ) 返回到行首而不前进到下一行。
Python 3
for x in range(10):
print(x, end='
')
print()
Python 2.7 向前兼容
from __future__ import print_function
for x in range(10):
print(x, end='
')
print()
Python 2.7
for x in range(10):
print '{}
'.format(x),
print
Python 2.0-2.6
for x in range(10):
print '{0}
'.format(x),
print
在后两种情况下(仅限 Python 2),print 语句末尾的逗号指示不要转到下一行。最后一个 print 语句前进到下一行,因此提示不会覆盖最终输出。
管线清洁
如果您不能保证新行文本不短于现有行,那么您只需添加“清除到行尾”转义序列'x1b[1K'
('x1b'
= ESC):
for x in range(75):
print('*' * (75 - x), x, end='x1b[1K
')
print()
长线换行
所有这些方法都假设您输入的内容不超过行的长度。回车符只会返回到当前行的开头,因此如果您的输出超过一行,您只会删除最后一行。
如果这个问题严重到需要控制,您可以禁用换行功能,以防止光标换行到下一行。(相反,光标会停留在行尾,并且连续的字符会覆盖。)
使用 禁用换行print('x1b[7l', end='')
,使用 重新启用换行print('x1b[7h', end='')
。请注意,任何时候都不会自动重新启用换行:如果程序因异常而终止,请不要让终端处于中断状态!
解决方案 2:
由于我是通过 Google 来到这里的,但我使用的是 Python 3,因此下面是它在 Python 3 中的工作方式:
for x in range(10):
print("Progress {:2.1%}".format(x / 10), end="
")
相关答案在这里:如何在打印语句后抑制换行符?
解决方案 3:
@Mike DeSimone 的答案可能在大多数情况下都有效。但是...
for x in ['abc', 1]:
print '{}
'.format(x),
-> 1bc
这是因为`'
'`仅仅返回到行首但不会清除输出。
如果 POSIX 支持对您来说足够了,以下命令将清除当前行并将光标保留在其开头:
print 'x1b[2K
',
它使用 ANSI 转义码来清除终端行。更多信息可以在 wikipedia和这篇精彩演讲中找到。
其他方法
我发现的另一个(可能更糟糕的)解决方案如下所示:
last_x = ''
for x in ['abc', 1]:
print ' ' * len(str(last_x)) + '
',
print '{}
'.format(x),
last_x = x
-> 1
一个优点是它也可以在 Windows 上运行。
解决方案 4:
在访问此主题之前,我也有同样的问题。对我来说,sys.stdout.write 只有在我正确刷新缓冲区时才有效,即
for x in range(10):
sys.stdout.write('
'+str(x))
sys.stdout.flush()
如果不刷新,结果只会打印在脚本的末尾
解决方案 5:
抑制换行符并打印`
`。
print 1,
print '
2'
或者写入标准输出:
sys.stdout.write('1')
sys.stdout.write('
2')
解决方案 6:
这是 @Nagasaki45 答案的一个更简洁、更“即插即用”的版本。与此处的许多其他答案不同,它可以正确处理不同长度的字符串。它通过清除行来实现这一点,清除的空格数与打印的最后一行的长度相同。也适用于 Windows。
def print_statusline(msg: str):
last_msg_length = len(getattr(print_statusline, 'last_msg', ''))
print(' ' * last_msg_length, end='
')
print(msg, end='
')
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)
解决方案 7:
for x in range(10):
time.sleep(0.5) # shows how its working
print("
{}".format(x), end="")
time.sleep(0.5) 用于显示如何删除先前的输出,并且当在打印消息的开头时如何打印新的输出“\r”,它将在新输出之前删除先前的输出。
解决方案 8:
尝试一下:
import time
while True:
print("Hi ", end="
")
time.sleep(1)
print("Bob", end="
")
time.sleep(1)
它对我有用。该 `end="
"`部分使它覆盖前一行。
警告!
如果您打印出,然后使用hi
打印出,您将得到因为输出覆盖了前两个字母。如果您打印出带有空格(此处未显示)的 ,则它将输出。要解决此问题,请使用 打印出空格。hello
`hillo
hihi
`
解决方案 9:
这适用于 Windows 和 Python 3.6
import time
for x in range(10):
time.sleep(0.5)
print(str(x)+'
',end='')
解决方案 10:
我无法让此页面上的任何解决方案适用于IPython,但对@Mike-Desimone 的解决方案稍加改动就可以完成工作:不是以回车符终止行,而是以回车符开始行:
for x in range(10):
print '
{0}'.format(x),
此外,这种方法不需要第二个打印语句。
解决方案 11:
接受的答案并不完美。首先打印的行将保留在那里,如果第二次打印未覆盖整个新行,则最终将得到垃圾文本。
为了说明问题,请将此代码保存为脚本并运行它(或者只是看一下):
import time
n = 100
for i in range(100):
for j in range(100):
print("Progress {:2.1%}".format(j / 100), end="
")
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="
")
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%
解决方案 12:
我有点惊讶没有人使用退格键。下面是一个使用它的程序。
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('')
解决方案 13:
这是我的解决方案!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 = '[K'
ERASE_LINE = 'x1b[2K'
sys.stdout.write(CURSOR_UP_ONE)
sys.stdout.write(ERASE_LINE+'
')
print(msg, end='
')
#example
ig_usernames = ['beyonce','selenagomez']
for name in ig_usernames:
clearline("SCRAPING COMPLETE: "+ name)
输出-每一行都将被重写,不显示任何旧文本:
SCRAPING COMPLETE: selenagomez
下一行(在同一行完全重写):
SCRAPING COMPLETE: beyonce
解决方案 14:
根据前面的答案再回答一个。
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('
')
sys.stdout.write(' '*shutil.get_terminal_size((80, 20)).columns)
sys.stdout.write('
')
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('
')
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('
') # without
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)
解决方案 15:
(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(" ", end="") # the trick!
time.sleep(0.2) # so you can see what it's doing
解决方案 16:
最好覆盖整行,否则如果新行较短,新行将与旧行混合。
import time, os
for s in ['overwrite!', 'the!', 'whole!', 'line!']:
print(s.ljust(os.get_terminal_size().columns - 1), end="
")
time.sleep(1)
必须columns - 1
在 Windows 上使用。
解决方案 17:
这对我有用,在 Windows 中使用 Spyder 中的 Python 3.7.9:
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='
')
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")
解决方案 18:
无论如何,如果有人想覆盖(清除)之前在 stdout 中打印的许多 行,那么这个答案应该对他有帮助。(感谢 Thijmen Dam 的精彩文章《覆盖以前打印的行》)
在 ANSI 控制台中您可以使用特殊序列:
[1A
和[K
第一个是抬起光标,第二个是完全删除一行。
清除控制台的示例(Python 3):
LINE_UP = '[1A'
LINE_CLEAR = '[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('[2J', end='', flush=True)
如果你只想定位光标,那么使用这个:
print('[<L>;<C>f', end='', flush=True)
其中<L>
和<C>
分别表示线和列。
为您提供一些 ANSI 转义序列的参考
解决方案 19:
根据我在该主题中看到的回复,我制作了一个适用于所有平台的小类。
import sys
import time
class PrintSameLine:
"""Class to correctly print on same line.
"""
def __init__(self):
self.last_message = ""
def print_msg(self, msg: str):
print(" " * len(self.last_message), end="
", flush=True)
print(msg, end="
", flush=True)
self.last_message = msg
# Note printing in reverse so you can see how it works
prnt = PrintSameLine()
for i in reversed(range(10)):
time.sleep(0.5)
prnt.print_msg("*" * i)
解决方案 20:
我尝试了这个方法,效果很好
import time
for i in range(10):
print(f'
{i}',end='')
time.sleep(1)
解决方案 21:
我知道这个问题之前已经有人回答过了,但我找不到一个优雅的解决方案来清除比下一行长的前几行。我正在打印出文件状态,如下所示:
Reading: file1.txt
Adding: file1.txt
Done: file1.txt
因此我创建了一个类,保留了之前的类,并在必要时替换它
class Printer:
'''
Clears the previous line and prints the next.
'''
max_length: int = 0
def print_replace(self, nextline):
'''replace the previous line'''
print(f'{nextline} {' ' * max(0, self.max_length - len(nextline))}', end='
')
self.max_length = max(self.max_length, len(nextline))
def print_new(self, nextline):
'''adds to the print stream'''
print(f'{nextline} {' ' * max(0, self.max_length - len(nextline))}')
self.max_length = max(self.max_length, len(nextline))
因此,在读取和写入文件时,它会更新同一行,直到完成。然后完成之后会出现另一行。最后看起来像这样:
Done file1.txt
Done file2.txt
Done file3.txt
Done file4.txt
Done file5.txt
Reading file6.txt
而不是:
Done file1.txttxt
Done file2.txttxt
Done file3.txttxt
Done longer-filename4.txt
Done file5.txtlename4.txt
Reading file6.txt
这样做的好处之一是它不需要睡眠。
printer = Printer()
for x in range(1, 10):
printer.print_replace(f'Reading file{x}.txt')
printer.print_replace(f'Writing file{x}.txt')
printer.print_new(f'Done file{x}.txt')
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理必备:盘点2024年13款好用的项目管理软件