จะเขียนทับการพิมพ์ก่อนหน้านี้เป็น stdout ใน python ได้อย่างไร?


114

หากฉันมีรหัสต่อไปนี้:

for x in range(10):
     print x

ฉันจะได้ผลลัพธ์ของ

1
2
etc..

สิ่งที่ฉันต้องการทำคือแทนที่จะพิมพ์ขึ้นบรรทัดใหม่ฉันต้องการแทนที่ค่าก่อนหน้าและเขียนทับด้วยค่าใหม่ในบรรทัดเดียวกัน



ตามบันทึกด้านข้างให้แน่ใจว่าคุณได้รับคำตอบซึ่งสามารถล้างทั้งบรรทัดได้หากบรรทัดก่อนหน้ายาวกว่าบรรทัดปัจจุบัน
ผลไม้

คำตอบ:


120

วิธีหนึ่งคือใช้'\r'อักขระreturn ( ) เพื่อกลับไปที่จุดเริ่มต้นของบรรทัดโดยไม่ต้องไปที่บรรทัดถัดไป:

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-stringsอ่านได้ดีกว่า:

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

ใช้งานได้ แต่ฉันไม่เข้าใจสองส่วน ... '{0}' และ. format
ccwhite1

7
ที่แนะนำใน Python 2.6 และ (ฉันบอกแล้ว) เป็นวิธีมาตรฐานในการจัดรูปแบบสตริงใน Python 3 ตัว%ดำเนินการเลิกใช้งานแล้ว โดยพื้นฐานแล้วสตริงทั้งหมดมีวิธีการจัดรูปแบบ ในกรณีนี้{0}หมายถึง "อาร์กิวเมนต์แรกถึงformat()" (การนับเริ่มต้นที่ 0)
Mike DeSimone

ไม่ใช่ตัวเลือกหากคุณต้องใช้ซอฟต์แวร์เฉพาะ Windows และไม่มีสับเพื่อเรียกใช้ VM
Mike DeSimone

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 แบบเต็มพร้อมการควบคุมเคอร์เซอร์และการรองรับสี
Mike DeSimone

1
@PhilMacKay: ฉันลองใช้ python บน Mac ของฉัน: ใน [3]: พิมพ์ "Thing to erase \ r",; เวลานอนหลับ (1); พิมพ์ "-------------- \ r", -------------- ฉันคิดว่าปัญหาของคุณคือ Windows และบรรทัดที่แตกต่างกัน ลองสิ่งนี้: import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr')));ควรพิมพ์รหัสฐานสิบหกของลำดับอักขระเพื่อไปที่จุดเริ่มต้นของบรรทัด
Mike DeSimone

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 ชี้สิ่งนี้
Eric Uldall

15
ในโน้ตบุ๊ก ipython ฉันต้องทำ:print("\r", message, end="")
grisaitis

1
วิธีนี้ใช้งานได้ดีไม่ลบบรรทัด แต่ใช้ช่องว่างเพียงพอในการพิมพ์ต่อไปนี้จะได้งาน (แม้ว่าจะไม่สวยหรู) ความคิดเห็นหนึ่งที่ฉันอาจเพิ่มคือสิ่งนี้ใช้ไม่ได้ในคอนโซลดีบักของ VS Code แต่ใช้งานได้ในเทอร์มินัล
PhilMacKay

32

คำตอบของ @Mike DeSimone อาจใช้งานได้เกือบตลอดเวลา แต่...

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

-> 1bc

เนื่องจากการ'\r'ย้อนกลับไปที่จุดเริ่มต้นของบรรทัดเท่านั้น แต่ไม่ได้ล้างเอาต์พุต

แก้ไข: ทางออกที่ดีกว่า (กว่าข้อเสนอเก่าของฉันด้านล่าง)

หากการสนับสนุน 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

ข้อดีอย่างหนึ่งคือมันจะทำงานบน windows ด้วย


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

ผลลัพธ์จะถูกพิมพ์เฉพาะในตอนท้ายของสคริปต์เท่านั้น


นอกจากนี้ควรเติมช่องว่างที่เหลือของสตริงเช่นstringvar.ljust(10,' ')ในกรณีของสตริงที่มีความยาวผันแปรได้
Annarfych

@chris ดอน et al. นี่คือ Python 2 คำตอบอื่น ๆ ส่วนใหญ่คือ Python 3 ทุกคนควรใช้ Python 3 ในขณะนี้ Python 2 สิ้นสุดอายุการใช้งานในปี 2020
smci

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
แต่คำตอบของ Rubixred ได้ผล แปลก. พวกเขาดูเหมือนวิธีเดียวกันกับฉัน ความแตกต่างเดียวที่ฉันเห็นคือ\rอยู่ที่จุดเริ่มต้นแทนที่จะเป็นจุดสิ้นสุด
PaulMag

7

ฉันไม่สามารถหาวิธีแก้ปัญหาใด ๆ ในหน้านี้เพื่อใช้กับIPythonได้ แต่การเปลี่ยนแปลงเล็กน้อยในโซลูชันของ @ Mike-Desimone ได้ผล: แทนที่จะยุติบรรทัดด้วยการกลับแคร่ให้เริ่มบรรทัดด้วยการกลับแคร่:

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

นอกจากนี้วิธีนี้ไม่จำเป็นต้องใช้คำสั่งพิมพ์ครั้งที่สอง


แม้ว่าจะใช้ไม่ได้ใน PyDev console หรือไม่ใช้เครื่องหมายจุลภาคหลังวงเล็บปิดของ print ()
Noumenon

ไม่ได้ผลสำหรับฉันเพียงแค่เขียนผลลัพธ์สุดท้าย (เช่น: "ดาวน์โหลดความคืบหน้า 100%") เมื่อดำเนินการเสร็จสิ้น
Alexandre

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" เมื่อเริ่มต้นข้อความการพิมพ์ระบบจะลบเอาต์พุตก่อนหน้านี้ออกก่อนเอาต์พุตใหม่



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

นี่คือคำตอบ "Plug-and-play" ที่สะอาดกว่าและเป็นเวอร์ชั่นของ @ 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

ฉันแปลกใจเล็กน้อยที่ไม่มีใครใช้อักขระ backspace นี่คือสิ่งที่ใช้มัน

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

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)

1

นี่คือทางออกของฉัน! Windows 10, Python 3.7.1

ฉันไม่แน่ใจว่าทำไมรหัสนี้ถึงใช้งานได้ แต่มันจะลบบรรทัดเดิมทั้งหมด ฉันรวบรวมจากคำตอบที่แล้ว คำตอบอื่น ๆ ก็จะกลับมาเส้นเริ่มต้น แต่ถ้าคุณมีสายสั้นหลังจากนั้นก็จะมีลักษณะ messed ขึ้นเหมือนจะกลายเป็นhellobyelo

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.