Làm cách nào để ghi đè bản in trước đó thành stdout trong python?


113

Nếu tôi có mã sau:

for x in range(10):
     print x

Tôi sẽ nhận được đầu ra của

1
2
etc..

Điều tôi muốn làm là thay vì in một dòng mới, tôi muốn thay thế giá trị trước đó và ghi đè nó bằng giá trị mới trên cùng một dòng.



Một lưu ý phụ, hãy đảm bảo bạn nhận được câu trả lời có thể tuôn ra toàn bộ dòng nếu dòng trước dài hơn dòng hiện tại.
Trái cây

Câu trả lời:


119

Một cách là sử dụng '\r'ký tự xuống dòng ( ) để trở về đầu dòng mà không chuyển sang dòng tiếp theo:

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

Dấu phẩy ở cuối câu lệnh in cho biết nó không chuyển sang dòng tiếp theo. Câu lệnh in cuối cùng chuyển sang dòng tiếp theo để lời nhắc của bạn không ghi đè đầu ra cuối cùng của bạn.

Cập nhật

Bây giờ Python 2 là EOL, một câu trả lời Python 3 có ý nghĩa hơn. Đối với Python 3.5 trở về trước:

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

Trong Python 3.6 trở lên, f-string đọc tốt hơn:

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

Tác phẩm này, nhưng tôi không hiểu một vài trong những phần ... '{0}' và .format
ccwhite1

7
Điều đó được giới thiệu trong Python 2.6 và (tôi đã nói) là cách tiêu chuẩn để định dạng chuỗi trong Python 3; các %nhà điều hành bị phản đối. Về cơ bản, tất cả các chuỗi hiện có một phương thức định dạng . Trong trường hợp này, {0}nghĩa là "đối số đầu tiên đến format()" (đếm bắt đầu từ 0).
Mike DeSimone

Không phải là một tùy chọn nếu bạn phải sử dụng một số phần mềm chỉ dành cho Windows và không có đủ điều kiện để chạy một máy ảo.
Mike DeSimone

2
Và nếu bạn muốn trở thành đầu cuối chéo, re.sub(r'\$<\d+>[/*]?', '', curses.tigetstr('el') or '')sẽ cung cấp cho bạn chuỗi xóa từ đầu đến cuối dòng chính xác cho bất kỳ thiết bị đầu cuối nào mà người dùng có. Hãy nhớ khởi tạo cursesbằng một cái gì đó như curses.setupterm(fd=sys.stdout.fileno())và sử dụng sys.stdout.isatty()để đảm bảo rằng đầu ra của bạn không được chuyển hướng đến một tệp. Xem code.activestate.com/recipes/475116 để biết mô-đun Python đầy đủ với điều khiển con trỏ và hỗ trợ màu.
Mike DeSimone,

1
@PhilMacKay: Tôi đã thử điều đó trong python trên máy Mac của mình: Trong [3]: print "Thing to delete \ r" ,; time.sleep (1); print "-------------- \ r", -------------- Tôi nghĩ vấn đề của bạn là Windows và dòng khác của nó kết thúc. Hãy thử cách này: import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr')));Nó sẽ in các mã hex của chuỗi ký tự để đi đến đầu dòng.
Mike DeSimone

107

Vì tôi đã kết thúc ở đây thông qua Google nhưng đang sử dụng Python 3, đây là cách điều này sẽ hoạt động trong Python 3:

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

Câu trả lời liên quan ở đây: Làm cách nào để chặn dòng mới sau câu lệnh in?


3
Tôi đã thử điều này và nó hầu như hoạt động tốt. Vấn đề duy nhất là nó không xóa đầu ra trước đó trên dòng đó, ví dụ: nếu bản in vòng lặp đầu tiên của bạn testingvà vòng lặp thứ hai của bạn in testđầu ra sau lần vượt qua thứ hai vẫn sẽ như vậy testing- bây giờ tôi thấy rằng @ Nagasaki45 đã chỉ ra điều này
Eric Uldall

15
trong một cuốn sổ ghi chép ipython, tôi phải làm:print("\r", message, end="")
grisaitis

1
Điều này hoạt động tuyệt vời, nó không xóa dòng nhưng sử dụng đủ khoảng trắng trong bản in sau sẽ thực hiện công việc (mặc dù nó không thanh lịch). Một nhận xét mà tôi có thể thêm vào là điều này không hoạt động trong bảng điều khiển gỡ lỗi của VS Code, nhưng nó hoạt động trong thiết bị đầu cuối.
PhilMacKay

31

@Mike DeSimone câu trả lời có thể sẽ hoạt động hầu hết thời gian. Nhưng...

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

-> 1bc

Điều này là do '\r'chỉ quay trở lại đầu dòng nhưng không xóa đầu ra.

CHỈNH SỬA: Giải pháp tốt hơn (so với đề xuất cũ của tôi bên dưới)

Nếu hỗ trợ POSIX là đủ cho bạn, phần sau sẽ xóa dòng hiện tại và để con trỏ ở đầu dòng:

print '\x1b[2K\r',

Nó sử dụng mã thoát ANSI để xóa dòng đầu cuối. Có thể tìm thấy thêm thông tin trong wikipedia và trong bài nói chuyện tuyệt vời này .

Câu trả lời cũ

Giải pháp (không tốt lắm) mà tôi đã tìm thấy trông như thế này:

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

-> 1

Một ưu điểm là nó cũng sẽ hoạt động trên các cửa sổ.


1
Tôi đã tạo một hàm từ 'câu trả lời cũ' hoạt động bình thường (ngay cả trên Windows), hãy xem câu trả lời của tôi: stackoverflow.com/a/43952192/965332
erb

25

Tôi đã có cùng một câu hỏi trước khi truy cập chủ đề này. Đối với tôi, sys.stdout.write chỉ hoạt động nếu tôi làm sạch bộ đệm đúng cách, tức là

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

Không xả ra, kết quả chỉ được in ở cuối tập lệnh


Cũng là một ý tưởng hay để lấp đầy phần còn lại của chuỗi bằng khoảng trống như stringvar.ljust(10,' ')trong trường hợp chuỗi có độ dài thay đổi.
Annarfych

@chris, Don và cộng sự. đây là Python 2, hầu hết các câu trả lời khác là Python 3. Mọi người nên sử dụng Python 3 ngay bây giờ; Python 2 sẽ kết thúc vòng đời vào năm 2020.
smci

18

Bỏ dòng mới và in \r.

print 1,
print '\r2'

hoặc viết thư cho stdout:

sys.stdout.write('1')
sys.stdout.write('\r2')

9

Thử cái này:

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

Nó đã làm việc cho tôi. Phần end="\r"đang làm cho nó ghi đè lên dòng trước đó.

CẢNH BÁO!

Nếu bạn in ra hi, sau đó in ra hellobằng cách sử dụng \r, bạn sẽ nhận được hillovì đầu ra được viết đè lên hai chữ cái trước đó. Nếu bạn in ra hivới khoảng trắng (không hiển thị ở đây), thì nó sẽ xuất ra hi. Để khắc phục điều này, hãy in ra các khoảng trắng bằng cách sử dụng \r.


1
Điều này không hiệu quả với tôi. Tôi đã đối phó với mã đó, nhưng đầu ra của tôi là Hi BobHi BobHi BobHi BobHi Bob. Python 3.7.1
PaulMag

1
Nhưng câu trả lời của Rubixred đã hiệu quả. Kỳ dị. Tôi trông giống như cùng một phương pháp. Sự khác biệt duy nhất tôi thấy \rlà ở phần đầu thay vì phần cuối.
PaulMag

7

Tôi không thể tìm thấy bất kỳ giải pháp nào trên trang này hoạt động cho IPython , nhưng một biến thể nhỏ trong giải pháp của @ Mike-Desimone đã thực hiện công việc: thay vì kết thúc dòng bằng ký tự xuống dòng, hãy bắt đầu dòng bằng ký tự xuống dòng:

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

Ngoài ra, phương pháp này không yêu cầu câu lệnh in thứ hai.


Ngay cả điều đó không hoạt động trong bảng điều khiển PyDev, cũng như không có dấu phẩy sau dấu ngoặc đơn đóng của print ().
Noumenon,

không hiệu quả với tôi, nó chỉ ghi kết quả cuối cùng (ví dụ: "Tiến trình tải xuống 100%") sau khi quá trình hoàn tất.
Alexandre

2
@Alexandre Nó hoạt động tốt trong IPython hoặc Jupyter kế nhiệm. Bạn cần làm sạch bộ đệm. stackoverflow.com/questions/17343688/…
March Ho

7
for x in range(10):
    time.sleep(0.5) # shows how its working
    print("\r {}".format(x), end="")

time.sleep (0.5) là hiển thị cách đầu ra trước đó bị xóa và đầu ra mới được in "\ r" khi nó ở đầu thông báo in, nó sẽ xóa đầu ra trước đó trước đầu ra mới.


7

Điều này hoạt động trên Windows và python 3.6

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

5

Câu trả lời được chấp nhận là không hoàn hảo . Dòng được in đầu tiên sẽ ở đó và nếu bản in thứ hai của bạn không bao phủ toàn bộ dòng mới, bạn sẽ kết thúc với văn bản rác.

Để minh họa sự cố, hãy lưu mã này dưới dạng tập lệnh và chạy nó (hoặc chỉ cần xem qua):

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

Đầu ra sẽ giống như sau:

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

Những gì hiệu quả với tôi là xóa dòng trước khi để lại một bản in vĩnh viễn. Vui lòng điều chỉnh vấn đề cụ thể của bạn:

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

Và bây giờ nó in như mong đợi:

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

5

Đây là phiên bản "plug-and-play" gọn gàng hơn, hiệu quả hơn cho câu trả lời của @ Nagasaki45. Không giống như nhiều câu trả lời khác ở đây, nó hoạt động đúng với các chuỗi có độ dài khác nhau. Nó đạt được điều này bằng cách xóa dòng có nhiều khoảng trắng bằng chiều dài của dòng in cuối cùng. Cũng sẽ hoạt động trên 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

Sử dụng

Đơn giản chỉ cần sử dụng nó như thế này:

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

Thử nghiệm nhỏ này cho thấy rằng các dòng được xóa đúng cách, ngay cả với các độ dài khác nhau:

for i in range(9, 0, -1):
    print_statusline("{}".format(i) * i)
    time.sleep(0.5)

5
Đây là một giải pháp tuyệt vời vì nó thanh lịch làm sạch đầu ra trước theo chiều dài đầy đủ của nó không chỉ cần in một số bất kỳ không gian trắng Cảm ơn (đó là những gì trước đây tôi đã viện đến!)
Toby Petty

2

Tôi hơi ngạc nhiên là không ai sử dụng ký tự xóa lùi. Đây là một trong những sử dụng nó.

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

đảm bảo rằng bạn xả sạch sau khi sử dụng phương pháp này, nếu không bạn có thể không in được gì ra màn hình. sys.stdout.flush()
Gabriel Fair

2

(Python3) Đây là những gì đã làm việc cho tôi. Nếu bạn chỉ sử dụng \ 010 thì nó sẽ để lại các ký tự, vì vậy tôi đã chỉnh sửa nó một chút để đảm bảo nó ghi đè lên những gì ở đó. Điều này cũng cho phép bạn có một cái gì đó trước mục in đầu tiên và chỉ loại bỏ chiều dài của mục đó.

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

Thêm một câu trả lời dựa trên các câu trả lời trước đó.

Nội dung của 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()

Cách sử dụng tập lệnh:

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

Đây là giải pháp của tôi! Windows 10, Python 3.7.1

Tôi không chắc tại sao mã này hoạt động, nhưng nó xóa hoàn toàn dòng gốc. Tôi đã tổng hợp nó từ những câu trả lời trước. Các câu trả lời khác sẽ chỉ trả lại dòng về đầu, nhưng nếu bạn có một dòng ngắn hơn sau đó, nó sẽ trông lộn xộn như hellobiến thành 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)

Đầu ra - Mỗi dòng sẽ được viết lại mà không có bất kỳ văn bản cũ nào hiển thị:

SCRAPING COMPLETE: selenagomez

Dòng tiếp theo (được viết lại hoàn toàn trên cùng một dòng):

SCRAPING COMPLETE: beyonce
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.