Как перезаписать предыдущий вывод на стандартный вывод в Python?

114

Если бы у меня был следующий код:

for x in range(10):
     print x

Я бы получил результат

1
2
etc..

Я хотел бы вместо печати новой строки заменить предыдущее значение и перезаписать его новым значением в той же строке.

ccwhite1
источник
В качестве примечания: убедитесь, что вы получили ответ, который может очистить всю строку, если предыдущая строка длиннее текущей.
Фрукты

Ответы:

120

Один из способов - использовать '\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
7
Это введено в Python 2.6 и (как мне сказали) является стандартным способом форматирования строк в Python 3; %оператор является устаревшим. По сути, все строки теперь имеют метод форматирования . В данном случае это {0}означает «первый аргумент для format()» (отсчет начинается с 0).
Майк Дезимоун,
Не вариант, если вам нужно использовать программное обеспечение только для Windows и у вас нет возможностей для запуска виртуальной машины.
Майк Дезимоун,
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]: print "Thing to erase \ r",; time.sleep (1); print "-------------- \ 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="")
grisaitis
1
Это отлично работает, это не стирает строку, но использование достаточного количества пробелов в следующей печати делает работу (хотя это не элегантно). Один комментарий, который я мог бы добавить, заключается в том, что это не работает в консоли отладки VS Code, но работает в терминале.
PhilMacKay 09
32

Ответ @Mike DeSimone, вероятно, будет работать большую часть времени. Но...

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

-> 1bc

Это потому, что '\r' единственный возвращается к началу строки, но не очищает вывод.

РЕДАКТИРОВАТЬ: Лучшее решение (чем мое старое предложение ниже)

Если вам достаточно поддержки POSIX, следующая строка очистит текущую строку и оставит курсор в ее начале:

print '\x1b[2K\r',

Он использует escape-код ANSI для очистки строки терминала. Более подробную информацию можно найти в Википедии и в этом замечательном выступлении. .

Старый ответ

Найденное (не очень хорошее) решение выглядит так:

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

-> 1

Одним из преимуществ является то, что он будет работать и с окнами.

Nagasaki45
источник
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()

Без промывки результат печатается только в конце скрипта

user2793078
источник
Также неплохо заполнить оставшуюся часть строки пробелами, как stringvar.ljust(10,' ')в случае строк переменной длины.
Annarfych
@chris, Don et al. это Python 2, большинство других ответов - Python 3. Теперь все должны использовать Python 3; Срок службы Python 2
истечет
18

Подавить новую строку и напечатать \r.

print 1,
print '\r2'

или напишите в stdout:

sys.stdout.write('1')
sys.stdout.write('\r2')
Игнасио Васкес-Абрамс
источник
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 , но небольшая вариация решения @Mike-Desimone сработала: вместо завершения строки с помощью возврата каретки начните строку с возврата каретки:

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

Кроме того, этот подход не требует второго оператора печати.

Давид Маркс
источник
Даже это не работает в консоли PyDev, как и запятая после закрывающей скобки print ().
Noumenon
у меня не сработало, он просто записывает окончательный результат (например: «Прогресс загрузки 100%») после завершения процесса.
Александр
2
@Alexandre Он отлично работает в IPython или его преемнике Jupyter. Вам нужно очистить буфер. stackoverflow.com/questions/17343688/…
Март Хо
7
for x in range(10):
    time.sleep(0.5) # shows how its working
    print("\r {}".format(x), end="")

time.sleep (0.5) показывает, как стирается предыдущий вывод, а новый вывод печатается "\ r", когда он находится в начале сообщения печати, он стирает предыдущий вывод перед новым выводом.

Рубиксред
источник
7

Это работает в Windows и Python 3.6.

import time
for x in range(10):
    time.sleep(0.5)
    print(str(x)+'\r',end='')
Сиддхант Саданги
источник
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
Это отличное решение, поскольку оно элегантно очищает предыдущий вывод до его полной длины без печати произвольного количества пробелов (к чему я уже прибегал раньше!) Спасибо
Тоби Петти
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()
Gabriel Fair
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
RenegadeJr
источник
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)
Krisz
источник
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
kpossibles
источник
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.

Карл Чанг
источник