Python
python3
制御コード
CSI

Pythonで出力した文字を消す方法

現在行を消去するコード

del_line.py
import sys
sys.stdout.write("\033[2K\033[G")
sys.stdout.flush()

使用例

sample.py
import sys, time

for i in range(10):
    s = str(i)*(10-i)
    sys.stdout.write("\033[2K\033[G%s" % s)
    sys.stdout.flush()
    time.sleep(0.5)
print()

次の出力が0.5秒おきに前の出力を消して同じ行に表示されます。

0000000000
111111111
22222222
3333333
444444
55555
6666
777
88
9

解説

CSIというものを使えば、標準出力やエラー出力のコンソール画面を制御できます。
参考:https://en.wikipedia.org/wiki/ANSI_escape_code
文字を消すだけでなく、カーソル移動や文字の色を変えたりできるようになります。

del_line.pyの2行目の\033[2Kが上記WikiのCSI Codesにある"EL – Erase in Line"にあたり、カーソルのある行を消去するコマンドです。\033[がCSIを使うよ、というメッセージでCSIを使うときは必ず最初に出力します。
続く2が引数に相当します。

  • 0もしくは省略ならカーソル位置から行末までを消去
  • 1なら行頭からカーソル位置までを消去
  • 2ならカーソルのある行を消去

となります。del_line.pyでは行全体を消去しています。
最後のKでCSIの種類を決定します。

Wikiの説明にもある通り、"EL – Erase in Line"はカーソル位置を変えないという性質があるため、消去した行に続けて文字を出力する場合は、手動でカーソルを行頭に移動する必要があります。
これは"CHA – Cursor Horizontal Absolute"を使って実現できます。Wikiの説明で"Moves the cursor to column n (default 1)"とありますので、nを1もしくは省略してCSIコードを出力すればカーソルが1列目(行頭)に移動します。CSIコードはCSI n Gとなっていますので、CSIの部分を\033[に置き換えて\033[Gとします。

本来であれば、このままで文字が消えてほしいのですが、Pythonでは文字を出力する場合に一度バッファに保存しておいて、改行時に一度に出力する仕様になっています。そのため、ソースコード上で文字消去のCSIコードを出力させたつもりになっていても、実際の画面には反映されません。そこで、sys.stdout.flush()で明示的にバッファにたまった文字列を出力させで画面に反映させます。

以上でコンソールに出力させた文字を消すことができます。

一文字だけ消す場合

〜2017/9/20追記〜
同じようにして、一文字だけ消す場合は次のようになります。

print("abc\033[1D\033[K")

出力結果

ab

\033[1Dでカーソルを一つ左に動かし、\033[Kでカーソルから行末まで削除しています。

補足

  • 連続で表示させる文字列の長さが同じだったり、あとの出力のほうが長い場合は、消去コマンドなしでカーソルを行頭に移動するだけで上書きすればOKです。
  • "EL – Erase in Line"ではカーソルのある行を消去するので、直前の出力で最後に改行している場合は、"CUU – Cursor Up"や"CPL – Cursor Previous Line"を使ってカーソルを一つ上に移動する必要があります。
  • 出力をファイルにリダイレクトするとファイルにCSIコードが記録されます。そのため、エディタ等では非常に読みにくくなりますが、catコマンドなどでコンソールに出力させると、実際の表示を再現できます。
  • CSIコードはPythonに限ったものではないので、他のプログラミング言語やシェルスクリプトでも同じCSIコードを出力させればコンソールを同じように制御できます。