เอาต์พุตไปยังบรรทัดเดียวกันเขียนทับเอาต์พุตก่อนหน้าหรือไม่


104

ฉันกำลังเขียนโปรแกรมดาวน์โหลด FTP ส่วนหนึ่งของรหัสมีลักษณะดังนี้:

ftp.retrbinary("RETR " + file_name, process)

ฉันกำลังเรียกใช้กระบวนการฟังก์ชั่นเพื่อจัดการการโทรกลับ:

def process(data):
    print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    file.write(data)

และผลลัพธ์เป็นดังนี้:

1784  KB / KB 1829 downloaded!
1788  KB / KB 1829 downloaded!
etc...   

แต่ฉันต้องการให้พิมพ์บรรทัดนี้และในครั้งต่อไปพิมพ์ / รีเฟรชใหม่ดังนั้นมันจะแสดงเพียงครั้งเดียวและฉันจะเห็นความคืบหน้าของการดาวน์โหลดนั้น

ทำได้ยังไง?


ซ้ำของแถบความคืบหน้าข้อความในคอนโซล
Alexey

คำตอบ:


202

นี่คือรหัสสำหรับ Python 3.x:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!', end='\r')

end=คำหลักคือสิ่งที่จะทำงานที่นี่ - โดยค่าเริ่มต้นprint()ปลายในการขึ้นบรรทัดใหม่ ( \n) ตัวละคร แต่สามารถถูกแทนที่ด้วยสตริงที่แตกต่างกัน ในกรณีนี้การสิ้นสุดบรรทัดด้วยการกลับแคร่แทนจะคืนเคอร์เซอร์ไปที่จุดเริ่มต้นของบรรทัดปัจจุบัน ดังนั้นจึงไม่จำเป็นต้องนำเข้าsysโมดูลสำหรับการใช้งานง่ายๆประเภทนี้ print()มีอาร์กิวเมนต์คำหลักจำนวนมากซึ่งสามารถใช้เพื่อลดความซับซ้อนของโค้ดได้มาก

หากต้องการใช้รหัสเดียวกันบน Python 2.6+ ให้วางบรรทัดต่อไปนี้ที่ด้านบนสุดของไฟล์:

from __future__ import print_function

6
โปรดทราบว่าฟังก์ชั่นการพิมพ์นอกจากนี้ยังสามารถนำมาใช้ในหลาม 2.6+ from __future__ import print_functionโดยการเพิ่มการนำเข้าต่อไปที่ด้านบนของแฟ้ม:
janin

12
ใน python <3.0 เครื่องหมายจุลภาคในตอนท้ายของคำสั่งจะป้องกัน "\ n": print "foo",อย่างไรก็ตามคุณยังคงต้องล้างหลังจากนั้นเพื่อดูการเปลี่ยนแปลง:sys.stdout.flush()
Tobias Domhan

32
ฉันพบว่าฉันจำเป็นต้องรวมไว้\rที่จุดเริ่มต้นของสตริงและตั้งค่าend=''แทนเพื่อให้สิ่งนี้ทำงานได้ ฉันไม่คิดว่าเทอร์มินัลของฉันชอบเมื่อฉันลงท้ายด้วย\r
Jezzamon

4
ในปัจจุบันทั้งหมดหลาม 3 รุ่นคุณสามารถเพิ่มflush=Trueไปยังprint()ฟังก์ชั่นเพื่อให้แน่ใจว่าบัฟเฟอร์ล้าง (บัฟเฟอร์บรรทัดมาตรฐานเท่านั้นที่จะล้างเมื่อมี\nการขึ้นบรรทัดใหม่จะถูกเขียน)
Martijn Pieters

4
ในสมุดบันทึก jupyter โดยใช้\rที่จุดเริ่มต้นและend=''ทำงานได้ดีที่สุดและราบรื่นที่สุด ใช้flush=Trueกับ\rท้ายถ้าสตริงและend='\r'ยังใช้งานได้ แต่ไม่ราบรื่น & ลบบรรทัด & ไลน์ฟีด
Dave X

41

\rหากสิ่งที่คุณต้องการจะทำคือการเปลี่ยนเส้นเดียวใช้ \rหมายถึงการกลับรถ เอฟเฟกต์คือทำให้คาเร็ตกลับมาที่จุดเริ่มต้นของบรรทัดปัจจุบันเท่านั้น มันไม่ได้ลบอะไรเลย ในทำนองเดียวกัน\bสามารถใช้เพื่อย้อนกลับหนึ่งอักขระ (เทอร์มินัลบางเครื่องอาจไม่รองรับคุณสมบัติเหล่านั้นทั้งหมด)

import sys

def process(data):
    size_str = os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    sys.stdout.write('%s\r' % size_str)
    sys.stdout.flush()
    file.write(data)

1
ฉันจะเพิ่มนั่น\rหมายถึงการกลับรถ เอฟเฟกต์ของมันคือทำให้คาเร็ตกลับมาที่จุดเริ่มต้นของบรรทัดปัจจุบันเท่านั้น มันไม่ได้ลบอะไรเลย ในทำนองเดียวกัน\bสามารถใช้เพื่อย้อนกลับหนึ่งอักขระ (เทอร์มินัลบางเครื่องอาจไม่รองรับคุณสมบัติเหล่านั้นทั้งหมด)
Adrien Plisson

sys.stdout.write('%s\r', size_str')ฉันคิดว่าคุณหมายถึงsys.stdout.write('%s\r' % size_str)แต่มันไม่ได้ผล
Kristian

@Adrien: เจ๋งเสริมว่า @ คริสเตียน: ขอบคุณอัปเดตว่ามันค่อนข้างช้า
Sam Dolan

โปรดทราบว่าคุณยังสามารถใช้printแทนได้sys.stdout.writeหากต้องการ: ใช้print size_str + "\r",งานได้ดี ,ในตอนท้ายของคำสั่งพิมพ์ยับยั้งการขึ้นบรรทัดใหม่; sys.stdout.flush()ยังคงเป็นที่ต้องการ
Jeff

19

มีลักษณะที่เป็นเอกสารคำสาปโมดูลและสาปแช่ง HOWTO

ตัวอย่างพื้นฐานจริงๆ:

import time
import curses

stdscr = curses.initscr()

stdscr.addstr(0, 0, "Hello")
stdscr.refresh()

time.sleep(1)

stdscr.addstr(0, 0, "World! (with curses)")
stdscr.refresh()

10
น่าเสียดายที่คำสาปมีให้บริการบน Unix เท่านั้น OP ไม่ได้บอกเราว่าแอปพลิเคชันของเขากำหนดเป้าหมายระบบปฏิบัติการใด ...
Adrien Plisson

ฉันคุ้นเคยกับการทำงานภายใต้ Linux มากจนไม่ได้สังเกตว่ามีคำเตือนเกี่ยวกับโมดูลสำหรับ UNIX ในเอกสารของโมดูลเท่านั้นขอบคุณที่ชี้ให้เห็น @Adrien
Andrea Spadaccini

3
@AdrienPlisson ฉันรู้ว่าคำถามนี้เกิดขึ้นเมื่อหลายปีก่อน แต่คุณสามารถรับ Curses บน Windows ได้จริง: lfd.uci.edu/~gohlke/pythonlibs/#curses
Cold Diamondz

เมื่อฉันวางสิ่งนี้ลงในล่าม Python ของฉันเทอร์มินัลของฉันกลายเป็น fubar การออกจากล่ามไม่ได้ช่วยอะไร WTF?!
allyourcode

ใช้clrtoeolสำหรับเมื่อสายที่สองคือสั้นกว่าครั้งแรก
Serge Stroobandt

9

นี่คือชั้นเรียนเล็ก ๆ ของฉันที่สามารถพิมพ์บล็อกข้อความซ้ำได้ มันจะล้างข้อความก่อนหน้าอย่างถูกต้องเพื่อให้คุณสามารถเขียนทับข้อความเก่าของคุณด้วยข้อความใหม่ที่สั้นกว่าโดยไม่ต้องสร้างความยุ่งเหยิง

import re, sys

class Reprinter:
    def __init__(self):
        self.text = ''

    def moveup(self, lines):
        for _ in range(lines):
            sys.stdout.write("\x1b[A")

    def reprint(self, text):
        # Clear previous text by overwritig non-spaces with spaces
        self.moveup(self.text.count("\n"))
        sys.stdout.write(re.sub(r"[^\s]", " ", self.text))

        # Print new text
        lines = min(self.text.count("\n"), text.count("\n"))
        self.moveup(lines)
        sys.stdout.write(text)
        self.text = text

reprinter = Reprinter()

reprinter.reprint("Foobar\nBazbar")
reprinter.reprint("Foo\nbar")

2
สิ่งนี้จะใช้ไม่ได้ใน Windows จนถึง Windows 10 เนื่องจาก Windows 10 เป็นหน้าต่างแรกที่รองรับอักขระควบคุมเหล่านั้นen.wikipedia.org/wiki/ANSI_escape_code
user136036

8

ฉันพบว่าสำหรับคำสั่งพิมพ์อย่างง่ายใน python 2.7 ให้ใส่เครื่องหมายจุลภาคต่อท้าย'\r'ไฟล์.

print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!\r',

ซึ่งสั้นกว่าโซลูชันอื่น ๆ ที่ไม่ใช่ python 3 แต่ยังดูแลรักษายากกว่าด้วย


1
Python 2.7.10 และมันไม่ได้พิมพ์อะไรเลย บางทีคุณสามารถรวบรวมออนไลน์และส่งลิงค์เพื่อดูว่ามันทำงานอย่างไร ... ?
Shougo Makishima

5

คุณสามารถเพิ่ม '\ r' ที่ท้ายสตริงบวกด้วยเครื่องหมายจุลภาคที่ส่วนท้ายของฟังก์ชันการพิมพ์ ตัวอย่างเช่น:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!\r'),

1
นี่คือ python 3? ถ้าเป็นเช่นนั้นสิ่งที่มีค่าเริ่มต้นendการตั้งค่าพารามิเตอร์\nเพื่อ u (...)KB downloaded!\r\nพิมพ์ ฉันผิดเหรอ?
อาร์

การใช้end = '\r'แทนช่วยแก้ไขปัญหาใน Python 3
Abhimanyu Pallavi Sudhir

4

ฉันใช้สปายเดอร์ 3.3.1 - windows 7 - python 3.6 แม้ว่าอาจไม่จำเป็นต้องใช้ฟลัช อ้างอิงจากโพสต์นี้ - https://github.com/spyder-ide/spyder/issues/3437

   #works in spyder ipython console - \r at start of string , end=""
import time
import sys
    for i in range(20):
        time.sleep(0.5)
        print(f"\rnumber{i}",end="")
        sys.stdout.flush()

3

ในการเขียนทับบรรทัดก่อนหน้าใน python ทั้งหมดที่คุณต้องการคือการเพิ่มend = '\ r'ให้กับฟังก์ชันการพิมพ์ทดสอบตัวอย่างนี้:

import time
for j in range(1,5):
   print('waiting : '+j, end='\r')
   time.sleep(1)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.