Bagaimana cara menimpa cetakan sebelumnya ke stdout dengan python?

113

Jika saya memiliki kode berikut:

for x in range(10):
     print x

Saya akan mendapatkan hasil dari

1
2
etc..

Yang ingin saya lakukan adalah daripada mencetak baris baru, saya ingin mengganti nilai sebelumnya dan menimpanya dengan nilai baru pada baris yang sama.

ccwhite1
sumber
kemungkinan duplikat Python - Hapus dan Ganti Item yang dicetak
Sven Marnach
Sebagai catatan tambahan, pastikan Anda mendapatkan jawaban yang mampu menyiram seluruh baris jika baris sebelumnya lebih panjang dari baris saat ini.
Buah

Jawaban:

119

Salah satu caranya adalah dengan menggunakan karakter carriage return ( '\r') untuk kembali ke awal baris tanpa melanjutkan ke baris berikutnya:

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

Koma di akhir pernyataan cetak memberitahu itu untuk tidak pergi ke baris berikutnya. Pernyataan cetak terakhir maju ke baris berikutnya sehingga prompt Anda tidak akan menimpa hasil akhir Anda.

Memperbarui

Sekarang Python 2 adalah EOL, jawaban Python 3 lebih masuk akal. Untuk Python 3.5 dan sebelumnya:

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

Di Python 3.6 dan yang lebih baru, f-string terbaca lebih baik:

for x in range(10):
    print(f'{x}\r', end="")
print()
Mike DeSimone
sumber
Ini berfungsi, tapi saya tidak mengerti beberapa bagian ... '{0}' dan .format
ccwhite1
7
Itu diperkenalkan di Python 2.6 dan (saya diberitahu) adalah cara standar untuk memformat string di Python 3; yang %operator usang. Pada dasarnya, semua string sekarang memiliki metode format . Dalam hal ini, {0}artinya "argumen pertama untuk format()" (penghitungan dimulai dari 0).
Mike DeSimone
Bukan pilihan jika Anda harus menggunakan beberapa perangkat lunak khusus Windows dan tidak memiliki kemampuan untuk menjalankan VM.
Mike DeSimone
2
Dan jika Anda ingin menjadi lintas-terminal, re.sub(r'\$<\d+>[/*]?', '', curses.tigetstr('el') or '')akan memberi Anda string hapus-ke-akhir-baris yang benar untuk terminal apa pun yang dimiliki pengguna. Ingatlah untuk menginisialisasi cursesdengan sesuatu seperti curses.setupterm(fd=sys.stdout.fileno()), dan gunakan sys.stdout.isatty()untuk memastikan keluaran Anda tidak diarahkan ke sebuah file. Lihat code.activestate.com/recipes/475116 untuk modul Python lengkap dengan kontrol kursor dan dukungan warna.
Mike DeSimone
1
@PhilMacKay: Saya mencobanya dengan python di Mac saya: Dalam [3]: print "Thing to erase \ r",; waktu tidur (1); print "-------------- \ r", -------------- Saya rasa masalah Anda adalah Windows dan baris yang berbeda berakhir. Coba ini: import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr')));Ini harus mencetak kode hex dari urutan karakter untuk pergi ke awal baris.
Mike DeSimone
107

Karena saya berakhir di sini melalui Google tetapi menggunakan Python 3, berikut ini cara kerjanya dengan Python 3:

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

Jawaban terkait di sini: Bagaimana saya bisa menyembunyikan baris baru setelah pernyataan cetak?

Pascal
sumber
3
Saya mencoba ini dan sebagian besar berfungsi dengan baik. Satu-satunya masalah adalah tidak menghapus keluaran sebelumnya pada baris itu, misalnya: jika loop pertama Anda mencetak testingdan loop kedua Anda mencetak testoutput setelah lintasan kedua akan tetap testing- sekarang saya melihat bahwa @ Nagasaki45 menunjukkan hal ini
Eric Uldall
15
dalam notebook ipython, saya harus melakukan:print("\r", message, end="")
grisaitis
1
Ini berfungsi dengan baik, tidak menghapus garis tetapi menggunakan cukup spasi putih pada cetakan berikut melakukan pekerjaan (meskipun tidak elegan). Satu komentar yang mungkin saya tambahkan adalah bahwa ini tidak berfungsi di konsol debug VS Code, tetapi berfungsi di terminal.
PhilMacKay
31

Jawaban @Mike DeSimone mungkin akan berhasil sebagian besar waktu. Tapi...

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

-> 1bc

Ini karena '\r'hanya kembali ke awal baris tetapi tidak menghapus keluaran.

EDIT: Solusi yang lebih baik (dari proposal lama saya di bawah)

Jika dukungan POSIX cukup untuk Anda, berikut ini akan menghapus baris saat ini dan meninggalkan kursor di awal:

print '\x1b[2K\r',

Ia menggunakan kode escape ANSI untuk menghapus jalur terminal. Info lebih lanjut dapat ditemukan di wikipedia dan dalam pembicaraan hebat ini .

Jawaban lama

Solusi (tidak begitu bagus) yang saya temukan terlihat seperti ini:

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

-> 1

Salah satu keuntungannya adalah ini akan berfungsi di windows juga.

Nagasaki 45
sumber
1
Saya membuat fungsi dari 'jawaban lama' yang berfungsi dengan baik (bahkan di Windows), lihat jawaban saya: stackoverflow.com/a/43952192/965332
erb
25

Saya memiliki pertanyaan yang sama sebelum mengunjungi utas ini. Bagi saya sys.stdout.write bekerja hanya jika saya benar menyiram buffer yaitu

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

Tanpa pembilasan, hasilnya hanya dicetak di akhir skrip

pengguna2793078
sumber
Ide bagus juga untuk mengisi sisa string dengan spasi seperti stringvar.ljust(10,' ')dalam kasus string dengan panjang variabel.
Annarfych
@ Chris, Don dkk. ini adalah Python 2, sebagian besar jawaban lainnya adalah Python 3. Setiap orang harus menggunakan Python 3 sekarang; Python 2 berakhir masa pakainya pada tahun 2020.
smci
18

Tekan baris baru dan cetak \r.

print 1,
print '\r2'

atau tulis ke stdout:

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

Coba ini:

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

Itu berhasil untuk saya. Bagian end="\r"tersebut membuatnya menimpa baris sebelumnya.

PERINGATAN!

Jika Anda mencetak hi, kemudian mencetak hellomenggunakan \r, Anda akan mendapatkan hillokarena output menulis di atas dua huruf sebelumnya. Jika Anda mencetak hidengan spasi (yang tidak muncul di sini), maka itu akan muncul hi. Untuk mengatasinya, cetak spasi menggunakan \r.

Sam Bernstein
sumber
1
Ini tidak berhasil untuk saya. Saya mengatasi kode itu, tetapi keluaran saya adalah Hi BobHi BobHi BobHi BobHi Bob. Python 3.7.1
PaulMag
1
Tetapi jawaban Rubixred berhasil. Aneh. Mereka terlihat seperti metode yang sama bagi saya. Satu-satunya perbedaan yang saya lihat \radalah di awal, bukan di akhir.
PaulMag
7

Saya tidak bisa mendapatkan solusi apa pun di halaman ini agar berfungsi untuk IPython , tetapi sedikit variasi pada solusi @ Mike-Desimone melakukan pekerjaan itu: alih-alih mengakhiri jalur dengan pengembalian kereta, mulailah baris dengan pengembalian kereta:

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

Selain itu, pendekatan ini tidak memerlukan pernyataan cetak kedua.

David Marx
sumber
Bahkan itu tidak bekerja di konsol PyDev, juga tidak ada tanda kurung tutup koma setelah print ().
Noumenon
tidak berhasil untuk saya, itu hanya menulis hasil akhir (mis: "Download progres 100%") setelah proses selesai.
Alexandre
2
@Alexandre Ini berfungsi dengan baik di IPython atau penggantinya Jupyter. Anda perlu membersihkan buffer. stackoverflow.com/questions/17343688/…
Maret Ho
7
for x in range(10):
    time.sleep(0.5) # shows how its working
    print("\r {}".format(x), end="")

time.sleep (0,5) adalah untuk menunjukkan bagaimana keluaran sebelumnya dihapus dan keluaran baru dicetak "\ r" ketika di awal pesan cetak, itu akan menghapus keluaran sebelumnya sebelum keluaran baru.

Rubixred
sumber
7

Ini berfungsi pada Windows dan python 3.6

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

Jawaban yang diterima tidaklah sempurna . Baris yang dicetak pertama akan tetap di sana dan jika cetakan kedua Anda tidak menutupi seluruh baris baru, Anda akan berakhir dengan teks sampah.

Untuk mengilustrasikan masalah, simpan kode ini sebagai skrip dan jalankan (atau lihat saja):

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

Outputnya akan terlihat seperti ini:

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

Apa yang berhasil bagi saya adalah menghapus garis sebelum meninggalkan cetakan permanen. Jangan ragu untuk menyesuaikan dengan masalah spesifik Anda:

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

Dan sekarang tercetak seperti yang diharapkan:

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

Berikut adalah versi jawaban @ Nagasaki45 yang lebih bersih, lebih "plug-and-play". Tidak seperti banyak jawaban lain di sini, ini berfungsi dengan baik dengan string dengan panjang berbeda. Ini mencapai ini dengan membersihkan garis dengan spasi sebanyak panjang baris terakhir yang dicetak. Juga akan bekerja di 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

Pemakaian

Cukup gunakan seperti ini:

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

Tes kecil ini menunjukkan bahwa garis dibersihkan dengan benar, bahkan untuk panjang yang berbeda:

for i in range(9, 0, -1):
    print_statusline("{}".format(i) * i)
    time.sleep(0.5)
erb
sumber
5
Ini adalah solusi yang bagus karena dengan elegan membersihkan keluaran sebelumnya hingga penuh tanpa hanya mencetak spasi putih dalam jumlah acak (yang sebelumnya saya gunakan!) Terima kasih
Toby Petty
2

Saya agak terkejut tidak ada yang menggunakan karakter spasi mundur. Ini salah satu yang menggunakannya.

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
sumber
pastikan Anda menyiram setelah menggunakan metode ini atau Anda mungkin tidak mendapatkan apa pun yang dicetak ke layar. sys.stdout.flush()
Gabriel Fair
2

(Python3) Inilah yang berhasil untuk saya. Jika Anda hanya menggunakan \ 010 maka itu akan meninggalkan karakter, jadi saya men-tweaknya sedikit untuk memastikan itu menimpa apa yang ada di sana. Ini juga memungkinkan Anda untuk memiliki sesuatu sebelum item cetakan pertama dan hanya menghapus panjang item.

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

Satu jawaban lagi berdasarkan jawaban yang ada.

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

Penggunaan skrip:

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

Inilah solusi saya! Windows 10, Python 3.7.1

Saya tidak yakin mengapa kode ini berfungsi, tetapi sepenuhnya menghapus baris aslinya. Saya menyusunnya dari jawaban sebelumnya. Jawaban lain hanya akan mengembalikan baris ke awal, tetapi jika Anda memiliki baris yang lebih pendek setelah itu, akan terlihat kacau seperti helloberubah menjadi 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)

Output - Setiap baris akan ditulis ulang tanpa ada teks lama yang menunjukkan:

SCRAPING COMPLETE: selenagomez

Baris berikutnya (ditulis ulang sepenuhnya pada baris yang sama):

SCRAPING COMPLETE: beyonce
kpossibles
sumber
0

Lebih baik menimpa seluruh baris jika tidak baris baru akan bercampur dengan yang lama jika baris baru lebih pendek.

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

Harus digunakan columns - 1di Windows.

Carl Chang
sumber