Wie überschreibe ich den vorherigen Druck auf stdout in Python?

113

Wenn ich den folgenden Code hätte:

for x in range(10):
     print x

Ich würde die Ausgabe von bekommen

1
2
etc..

Anstatt eine neue Zeile zu drucken, möchte ich den vorherigen Wert ersetzen und ihn durch den neuen Wert in derselben Zeile überschreiben.

ccwhite1
quelle
Mögliches Duplikat von Python - Entfernen und Ersetzen von
Sven Marnach
Stellen Sie als Randnotiz sicher, dass Sie die Antwort erhalten, mit der die gesamte Zeile geleert werden kann, wenn die vorherige Zeile länger als die aktuelle Zeile ist.
Obst

Antworten:

118

Eine Möglichkeit besteht darin, das '\r'Zeichen carriage return ( ) zu verwenden, um zum Zeilenanfang zurückzukehren, ohne zur nächsten Zeile überzugehen:

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

Das Komma am Ende der print-Anweisung weist darauf hin, dass nicht in die nächste Zeile gewechselt werden soll. Die letzte Druckanweisung geht zur nächsten Zeile über, damit Ihre Eingabeaufforderung Ihre endgültige Ausgabe nicht überschreibt.

Aktualisieren

Jetzt, da Python 2 EOL ist, ist eine Python 3-Antwort sinnvoller. Für Python 3.5 und früher:

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

In Python 3.6 und höher lesen sich F-Strings besser:

for x in range(10):
    print(f'{x}\r', end="")
print()
Mike DeSimone
quelle
Das funktioniert, aber ich verstehe einige der Teile nicht ... '{0}' und .format
ccwhite1
7
Dies wird in Python 2.6 eingeführt und ist (wie mir gesagt wurde) die Standardmethode zum Formatieren von Zeichenfolgen in Python 3. Der %Bediener ist veraltet. Grundsätzlich haben alle Zeichenfolgen jetzt eine Formatierungsmethode . In diesem Fall {0}bedeutet das "das erste Argument für format()" (die Zählung beginnt bei 0).
Mike DeSimone
Keine Option, wenn Sie nur Windows-Software verwenden müssen und nicht die nötigen Voraussetzungen haben, um eine VM auszuführen.
Mike DeSimone
2
Und wenn Sie terminalübergreifend sein möchten re.sub(r'\$<\d+>[/*]?', '', curses.tigetstr('el') or ''), erhalten Sie die richtige Zeichenfolge zum Löschen bis zum Zeilenende für jedes Terminal, über das der Benutzer verfügt. Denken Sie daran, cursesmit etwas wie zu initialisieren curses.setupterm(fd=sys.stdout.fileno())und sys.stdout.isatty()sicherzustellen, dass Ihre Ausgabe nicht in eine Datei umgeleitet wird. Unter code.activestate.com/recipes/475116 finden Sie ein vollständiges Python-Modul mit Cursorsteuerung und Farbunterstützung.
Mike DeSimone
1
@PhilMacKay: Ich habe das in Python auf meinem Mac versucht: In [3]: print "Thing to erase \ r" ,; time.sleep (1); print "-------------- \ r", -------------- Ich denke, Ihr Problem ist Windows und seine verschiedenen Zeilen enden. Versuchen import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr')));Sie Folgendes: Es sollten die Hex-Codes der Zeichenfolge gedruckt werden, um zum Zeilenanfang zu gelangen.
Mike DeSimone
106

Da ich über Google hier gelandet bin, aber Python 3 verwende, würde dies in Python 3 folgendermaßen funktionieren:

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

Verwandte Antwort hier: Wie kann ich den Zeilenumbruch nach einer Druckanweisung unterdrücken?

Pascal
quelle
3
Ich habe es versucht und es funktioniert meistens gut. Das einzige Problem ist, dass die vorherige Ausgabe in dieser Zeile nicht gelöscht wird, zum Beispiel: Wenn Ihr erster Schleifendruck testingund Ihr zweiter Schleifendruck testdie Ausgabe nach dem zweiten Durchgang weiterhin sind testing- jetzt sehe ich, dass @ Nagasaki45 darauf hingewiesen hat
Eric Uldall,
15
In einem Ipython-Notizbuch muss ich Folgendes tun:print("\r", message, end="")
Grisaitis
1
Dies funktioniert hervorragend, es löscht die Zeile nicht, aber die Verwendung von genügend Leerzeichen im folgenden Druck erledigt den Job (obwohl es nicht elegant ist). Ein Kommentar, den ich hinzufügen könnte, ist, dass dies nicht in der Debug-Konsole von VS Code funktioniert, sondern im Terminal.
PhilMacKay
31

@ Mike DeSimone Antwort wird wahrscheinlich die meiste Zeit funktionieren. Aber...

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

-> 1bc

Das liegt daran, dass die '\r' einzige nur zum Zeilenanfang zurückkehrt, aber die Ausgabe nicht löscht.

EDIT: Bessere Lösung (als mein alter Vorschlag unten)

Wenn Ihnen die POSIX-Unterstützung ausreicht, wird die aktuelle Zeile durch Folgendes gelöscht und der Cursor am Anfang belassen:

print '\x1b[2K\r',

Es verwendet ANSI-Escape-Code, um die Terminalleitung zu löschen. Weitere Informationen finden Sie in Wikipedia und in diesem großartigen Vortrag .

Alte Antwort

Die (nicht so gute) Lösung, die ich gefunden habe, sieht folgendermaßen aus:

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

-> 1

Ein Vorteil ist, dass es auch unter Windows funktioniert.

Nagasaki45
quelle
1
Ich habe aus der 'alten Antwort' eine Funktion gemacht, die ordnungsgemäß funktioniert (auch unter Windows). Siehe meine Antwort: stackoverflow.com/a/43952192/965332
erb
25

Ich hatte die gleiche Frage, bevor ich diesen Thread besuchte. Für mich hat das sys.stdout.write nur funktioniert, wenn ich den Puffer richtig geleert habe, dh

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

Ohne Leeren wird das Ergebnis nur am Ende des Skripts gedruckt

user2793078
quelle
Es ist auch eine gute Idee, den Rest der Zeichenfolge mit Leerzeichen zu füllen, wie stringvar.ljust(10,' ')bei Zeichenfolgen mit variabler Länge.
Annarfych
@chris, Don et al. Dies ist Python 2, die meisten anderen Antworten sind Python 3. Jeder sollte jetzt Python 3 verwenden. Python 2 ist das Ende des Lebens im Jahr 2020.
smci
18

Unterdrücken Sie die Newline und drucken Sie \r.

print 1,
print '\r2'

oder schreiben Sie an stdout:

sys.stdout.write('1')
sys.stdout.write('\r2')
Ignacio Vazquez-Abrams
quelle
9

Versuche dies:

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

Es hat bei mir funktioniert. Der end="\r"Teil bewirkt, dass die vorherige Zeile überschrieben wird.

WARNUNG!

Wenn Sie ausdrucken hi, dann ausdrucken helloverwenden \r, die Sie erhalten , hilloweil die Ausgabe die letzten beiden Buchstaben schrieb über. Wenn Sie himit Leerzeichen drucken (die hier nicht angezeigt werden), wird dies ausgegeben hi. Um dies zu beheben, drucken Sie Leerzeichen mit aus \r.

Sam Bernstein
quelle
1
Das funktioniert bei mir nicht. Ich habe diesen Code kopiert, aber meine Ausgabe ist Hi BobHi BobHi BobHi BobHi Bob. Python 3.7.1
PaulMag
1
Aber Rubixreds Antwort funktionierte. Seltsam. Sie sehen für mich wie die gleiche Methode aus. Der einzige Unterschied, den ich sehe, ist, dass er \ram Anfang statt am Ende ist.
PaulMag
7

Ich konnte keine der Lösungen auf dieser Seite für IPython verwenden , aber eine geringfügige Abweichung von der Lösung von @ Mike-Desimone hat den Job erledigt: Anstatt die Zeile mit dem Wagenrücklauf zu beenden, beginnen Sie die Zeile mit dem Wagenrücklauf:

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

Darüber hinaus erfordert dieser Ansatz nicht die zweite Druckanweisung.

David Marx
quelle
Auch das funktioniert weder in der PyDev-Konsole noch ein Komma nach der engen Klammer von print ().
Noumenon
hat bei mir nicht funktioniert, sondern schreibt nur das Endergebnis (z. B. "Download-Fortschritt 100%"), sobald der Vorgang abgeschlossen ist.
Alexandre
2
@Alexandre Es funktioniert gut in IPython oder seinem Nachfolger Jupyter. Sie müssen den Puffer leeren. stackoverflow.com/questions/17343688/…
März Ho
7
for x in range(10):
    time.sleep(0.5) # shows how its working
    print("\r {}".format(x), end="")

time.sleep (0.5) zeigt an, wie die vorherige Ausgabe gelöscht und die neue Ausgabe "\ r" gedruckt wird, wenn zu Beginn der Drucknachricht die vorherige Ausgabe vor der neuen Ausgabe gelöscht wird.

Rubixred
quelle
7

Dies funktioniert unter Windows und Python 3.6

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

Die akzeptierte Antwort ist nicht perfekt . Die Zeile, die zuerst gedruckt wurde, bleibt dort und wenn Ihr zweiter Druck nicht die gesamte neue Zeile abdeckt, erhalten Sie Mülltext.

Um das Problem zu veranschaulichen, speichern Sie diesen Code als Skript und führen Sie ihn aus (oder werfen Sie einen Blick darauf):

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

Die Ausgabe sieht ungefähr so ​​aus:

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

Was für mich funktioniert, ist, die Zeile zu löschen, bevor ich einen dauerhaften Druck hinterlasse. Sie können sich jederzeit an Ihr spezifisches Problem anpassen:

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

Und jetzt wird wie erwartet gedruckt:

Progress 0.0%
Progress 1.0%
Progress 2.0%
Progress 3.0%
mimoralea
quelle
5

Hier ist eine sauberere, "Plug-and-Play" -Version der Antwort von @ Nagasaki45. Im Gegensatz zu vielen anderen Antworten funktioniert es ordnungsgemäß mit Zeichenfolgen unterschiedlicher Länge. Dies wird erreicht, indem die Zeile mit genau so vielen Leerzeichen gelöscht wird wie die Länge der zuletzt gedruckten Zeile. Funktioniert auch unter 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

Verwendung

Verwenden Sie es einfach so:

for msg in ["Initializing...", "Initialization successful!"]:
    print_statusline(msg)
    time.sleep(1)

Dieser kleine Test zeigt, dass Linien auch für unterschiedliche Längen richtig gelöscht werden:

for i in range(9, 0, -1):
    print_statusline("{}".format(i) * i)
    time.sleep(0.5)
erb
quelle
5
Dies ist eine großartige Lösung, da die vorherige Ausgabe elegant in voller Länge bereinigt wird, ohne nur eine beliebige Anzahl von Leerzeichen zu drucken (worauf ich zuvor zurückgegriffen hatte!). Danke
Toby Petty
2

Ich bin ein bisschen überrascht, dass niemand das Backspace-Zeichen verwendet. Hier ist eine, die es verwendet.

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')
David Philipe Gil
quelle
Stellen Sie sicher, dass Sie nach dieser Methode spülen, da sonst möglicherweise nichts auf den Bildschirm gedruckt wird. sys.stdout.flush()
Gabriel Fair
2

(Python3) Das hat bei mir funktioniert. Wenn Sie nur \ 010 verwenden, bleiben Zeichen übrig, daher habe ich es ein wenig optimiert, um sicherzustellen, dass es überschreibt, was dort war. Auf diese Weise können Sie auch etwas vor dem ersten Druckelement haben und nur die Länge des Elements entfernen.

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
quelle
2

Eine weitere Antwort basierend auf den vorherigen Antworten.

Inhalt von 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()

Verwendung des Skripts:

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
quelle
1

Hier ist meine Lösung! Windows 10, Python 3.7.1

Ich bin nicht sicher, warum dieser Code funktioniert, aber er löscht die ursprüngliche Zeile vollständig. Ich habe es aus den vorherigen Antworten zusammengestellt. Die anderen Antworten würden die Zeile nur an den Anfang zurückbringen, aber wenn Sie danach eine kürzere Zeile hätten, würde sie wie helloverwandelt aussehen 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)

Ausgabe - Jede Zeile wird neu geschrieben, ohne dass ein alter Text angezeigt wird:

SCRAPING COMPLETE: selenagomez

Nächste Zeile (komplett in derselben Zeile neu geschrieben):

SCRAPING COMPLETE: beyonce
kpossibles
quelle