Як перезаписати попередній друк на stdout у python?


113

Якби у мене був такий код:

for x in range(10):
     print x

Я отримав би результат

1
2
etc..

Що я хотів би зробити, це замість того, щоб надрукувати новий рядок, я хочу замінити попереднє значення і замінити його новим значенням у тому ж рядку.



В якості бічної примітки переконайтеся, що ви отримаєте відповідь, яка зможе пропустити весь рядок, якщо попередній рядок довший за поточний.
Фрукти

Відповіді:


119

Один із способів - використовувати '\r'символ return ( ) carriage ( ) для повернення до початку рядка, не переходячи до наступного рядка:

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

7
Це введено в Python 2.6 і (мені кажуть) - це стандартний спосіб форматування рядків у Python 3; %оператор є застарілим. В основному всі рядки тепер мають метод форматування . У цьому випадку {0}означає "перший аргумент format()" (підрахунок починається з 0).
Майк Десимоне

Це не варіант, якщо вам потрібно скористатися деяким програмним забезпеченням для Windows і не маєте відбивачів для запуску VM.
Майк Десимоне

2
І якщо ви хочете бути крос-терміналом, re.sub(r'\$<\d+>[/*]?', '', curses.tigetstr('el') or '')ви дасте правильний рядок стирання до кінця рядка для будь-якого терміналу у користувача. Не забудьте ініціалізувати cursesщось подібне curses.setupterm(fd=sys.stdout.fileno())та використовувати, sys.stdout.isatty()щоб переконатися, що ваш вихід не перенаправляється на файл. Дивіться code.activestate.com/recipes/475116 для повного модуля Python з керуванням курсором та підтримкою кольорів.
Майк Десімоне

1
@PhilMacKay: Я спробував це в python на моєму Mac: У [3]: надрукувати "Річ стерти \ r",; час.спля (1); надрукувати "-------------- \ r", -------------- Я думаю, що вашою проблемою є Windows та її різні рядки закінчуються. Спробуйте це: import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr')));Він повинен надрукувати шістнадцяткові коди послідовності символів, щоб перейти до початку рядка.
Майк Десимоне

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 вказав на це
Ерік Улдалл,

15
в зошиті ipython, я повинен зробити:print("\r", message, end="")
грисаїт

1
Це чудово працює, це не стирає лінію, але використання достатньої кількості пробілів у наступному друку робить цю роботу (хоча це не елегантно). Один коментар, який я можу додати, - це те, що це не працює в консолі налагодження VS Code, але працює в терміналі.
PhilMacKay

31

@Mike DeSimone відповідь, ймовірно, буде працювати більшу частину часу. Але ...

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

-> 1bc

Це тому, що '\r' єдине повертається до початку рядка, але не очищає вихід.

EDIT: Краще рішення (ніж моя стара пропозиція нижче)

Якщо підтримка 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

Одна перевага полягає в тому, що він також буде працювати на вікнах.


1
Я зробив функцію з «старого відповіді» , яка працює належним чином (навіть на Windows), см моя відповідь: stackoverflow.com/a/43952192/965332
Єврорадіо

25

У мене було те саме питання, перш ніж відвідати цю тему. Для мене 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


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
Але відповідь Рубіксреда спрацювала. Дивно. Вони схожі на мене тим самим методом. Єдина відмінність, яку я бачу, - \rце на початку, а не в кінці.
PaulMag

7

Я не зміг отримати жодне з рішень на цій сторінці для роботи в IPython , але незначна зміна рішення @ Майка-Дезімоне зробила цю роботу: замість того, щоб припиняти лінію з поверненням каретки, запускайте рядок із поверненням каретки:

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

Крім того, цей підхід не вимагає другої заяви про друк.


Навіть це не працює в консолі PyDev, ні кома після близьких дужок print ().
Номенон

не працювало для мене, він просто записує кінцевий результат (наприклад: "Завантажити прогрес на 100%"), коли річ із процесом буде зроблена.
Олександр

2
@Alexandre Це прекрасно працює в IPython або його наступнику Юпітері. Вам потрібно промити буфер. stackoverflow.com/questions/17343688/…
березень Ho

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: імпортувати 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

0

Краще перезаписати весь рядок, інакше новий рядок буде змішуватися зі старим, якщо новий рядок коротший.

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

Довелося використовувати columns - 1 в Windows.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.