Pythonで以前の出力をstdoutに上書きする方法は?


113

次のコードがある場合:

for x in range(10):
     print x

私はの出力を取得します

1
2
etc..

私がやりたいのは、改行を印刷する代わりに、以前の値を置き換えて、同じ行の新しい値で上書きしたいです。



補足として、前の行が現在の行より長い場合に行全体をフラッシュできる答えが得られることを確認してください。
フルーツ

回答:


119

1つの方法は、キャリッジリターン('\r')文字を使用して、次の行に進めずに行の先頭に戻ることです。

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

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()にするために使用することを忘れないでください。カーソルコントロールとカラーサポートを備えた完全なPythonモジュールについては、code.activestate.com / recipes / 475116を参照してください。
Mike DeSimone

1
@PhilMacKay:私はMacのpythonでそれを試しました:[3]で: "Thing to erase \ r"を印刷します; time.sleep(1); print "-------------- \ r"、--------------あなたの問題はWindowsであり、その行末が異なると思います。これを試してください:import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr')));文字列の16進コードを印刷して、行の先頭に移動します。
Mike DeSimone 2017年

107

私はここでGoogle経由で終了しましたが、Python 3を使用しているので、これがPython 3でどのように機能するかを次に示します。

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

ここでの関連回答:印刷ステートメントの後の改行を抑制するにはどうすればよいですか?


3
私はこれを試しました、そしてそれはほとんどうまくいきます。唯一の問題は、たとえば、その行の以前の出力が消去されないことです。たとえば、最初のループ印刷testingと2番目のループ印刷testが2回目のパスの後の出力が残っている場合testing、@ Nagasaki45がこれを指摘していることがわかります
Eric Uldall

15
ipythonノートに、私がしなければならない:print("\r", message, end="")
grisaitis

1
これはうまく機能します。行は消去されませんが、次の印刷で十分な空白を使用するとうまくいきます(エレガントではありません)。私が付け加えるかもしれないコメントの1つは、これはVS Codeのデバッグコンソールでは機能しないが、ターミナルでは機能するということです。
PhilMacKay

31

@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

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

@クリス、ドンら。これはPython 2です。他のほとんどの答えはPython 3です。今は誰もがPython 3を使用しているはずです。Python 2は2020
サポート


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と、出力が前の2つの文字を上書きしたために得られます。プリントアウトする場合hiスペース(ここには表示されません)を付けと、が出力されますhi。これを修正するには、を使用してスペースを印刷します\r


1
これは私にはうまくいきません。そのコードに対処しましたが、出力は Hi BobHi BobHi BobHi BobHi Bobです。Python 3.7.1
PaulMag 2019

1
しかし、Rubixredの答えはうまくいきました。変だ。彼らは私には同じ方法のように見えます。私が見る唯一の違いは、それ\rが終わりではなく最初にあるということです。
PaulMag

7

このページのソリューションをIPythonで機能させることはできませんでしたが、@ Mike-Desimoneのソリューションを少し変更するだけでうまくいきました。改行で行を終了するのではなく、改行で行を開始します。

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

さらに、このアプローチでは、2番目の印刷ステートメントは必要ありません。


それでもPyDevコンソールでは機能せず、print()の閉じ括弧の後のカンマも機能しません。
Noumenon 2015

私にとってはうまくいきませんでした。プロセスが完了すると、最終結果(例:「ダウンロードの進行状況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

受け入れられた答えは完璧ではありません。最初に印刷された行はそのまま残り、2番目の印刷が新しい行全体をカバーしない場合、最終的に文字化けします。

問題を説明するために、このコードをスクリプトとして保存して実行します(または単に見てみます)。

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

これは、@ 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

誰もバックスペース文字を使用していないことに少し驚いています。これを使用するものは次のとおりです。

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()
ガブリエルフェア

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

以前の回答に基づくもう1つの回答。

pbar.pyの内容: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

このコードが機能する理由はわかりませんが、元の行が完全に消去されます。以前の回答からまとめました。他の答えは行を最初に戻しますが、後で短い行があると、にhello変わり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)

出力-各行は、古いテキストを表示せずに書き換えられます。

SCRAPING COMPLETE: selenagomez

次の行(完全に同じ行に書き直されます):

SCRAPING COMPLETE: beyonce
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.