カテゴリー

Python基礎

【python&色付け】ANSIでターミナルの出力に色をつけて見やすくする

2021年8月22日

こんな人にオススメ

pythonでターミナル上に出力をしているんだけど、出力分が全部白で見づらい!

ターミナルの文字に色をつけて見やすくする方法ってないの?

ということで、今回はターミナルで出力する文字に色をつける方法について解説する。通常、こんなことはしなくても問題ないだろうが、警告文などを赤にして見やすくできる。

プログラミング初心者だと出力文のどこがどうなっているのかわからないことが多い。だから、ここに注目してほしいって時に今回の方法が役に立つだろう。

付け加える内容はかなりシンプルなので気兼ねなく使用できるだろう。適用後のイメージは上の感じ。ただし、コードを動かす環境で反映されないこともあるのでそこは注意。

実行環境とpython環境は以下。

  • M1 MacBook Pro(13-inch, 2020)
  • macOS Big Sur バージョン11.5.2
  • ターミナル バージョン2.11
    • デフォルトが黒背景
    • デフォルトが白文字
  • Python 3.9.6
  • termcolor 1.1.0

今回は基本的にはmacの純正ターミナルを使用する。

スポンサーリンク
スポンサーリンク

運営者のメガネと申します。TwitterInstagramも運営中。

自己紹介はこちらから、お問い合わせはこちらからお願いいたします。

運営者メガネ

ANSIのエスケープシーケンスで色付け

色付けの方法は至ってシンプルで、色付けしたい文字列を「エスケープシーケンス」なるもので挟めばいい。上の画像の例だと、赤文字と緑文字と赤背景。

ANSIとは

そもそもANSIというのは「American National Standards Institute」の略で、ざっくりいうとアメリカ国内の工業規格の標準化を行なっている非営利団体のこと。日本だとJISに相当。

で、このANSIが決めているっぽいエスケープシーケンスが今回のメイン。さっきの出力だと以下のようにコードを書いてターミナルで動かせばいい。

# Terminalで出力すると色が変わる
# Code runnerだとエスケープシーケンスがそのまま出力される
# 出力の上側がTerminalで、出力文の色が変わる
# 出力の下側がCode Runnerで、\3がエスケープシーケンスとして表示される
print('\3[31m赤色\3[0m')
# 赤色
# [31m赤色[0m

# つなげて書くと見づらいので分離させておく
print('\3[31m' + '赤色' + '\3[0m')
# 赤色
# [31m赤色[0m

print('\3[32m' + '緑色' + '\3[0m')
# 緑色
# [32m緑色[0m

print('\3[41m' + '赤背景' + '\3[0m')
# 赤背景
# [41m赤背景[0m

\\033というのがエスケープシーケンスに相当するもので、これは8進数で書かれたESC記号。16進数にする場合は\\x1bとする。どちらでも良い。

そして、その後の[で始まりmで終わる部分の数字を変更することで色々な色を指定したり書式を変更したりすることができる。31だと赤文字で41だと赤背景。

また、この色付けはターミナルなどで動くもので、VScodeの拡張機能Code Runnerで「出力」部分に出力した場合は上のコードの各出力下のようにうまく表示されない。

一応、Code Runnerの設定でターミナルで表示するようにすると色付けは可能。しかし、そもそもmac純正ターミナルとVScodeの統合ターミナルで挙動が異なるのでVScodeでは反映されない書式もある。ややこしい。

こっそりエスケープシーケンスが隠れている。

この画像はCode RunnerでVScodeの「出力」部分に表示した出力結果。[の前に\\033に該当する「ESC」という文字が表示されているのがわかる。

確か、VScodeの設定でこのような小さい文字(エスケープシーケンス)の表示をいじれたはずだが見つからなかったのでスルー。

色のエスケープシーケンス一覧

上の画像が執筆者の環境で行なった、各エスケープシーケンスの出力結果。italicrapid_flashが反映されていなかったが、概ねちゃんとした挙動となった。VScodeの場合はitalicがうまくいったが、flashが反映されなかった。

各挙動とエケープシーケンスは以下。リセット用は別のdictで用意した。

# 色つけ用のdict
escapes = {
    'bold': '\3[01m',  # 太字
    'lighter': '\3[02m',  # 薄い色
    'italic': '\3[03m',  # 斜体
    'underline': '\3[04m',  # 下線
    'flash': '\3[05m',  # 点滅
    'rapid_flash': '\3[06m',  # 高速点滅
    'inverse': '\3[07m',  # 文字色と背景色を反転
    'invisible': '\3[08m',  # 見えない
    'strikethrough': '\3[09m',  # 取り消し線

    'font_black': '\3[30m',  # 黒文字
    'font_red': '\3[31m',  # 赤文字
    'font_green': '\3[32m',  # 緑文字
    'font_yellow': '\3[33m',  # 黄文字
    'font_blue': '\3[34m',  # 青文字
    'font_magenta': '\3[35m',  # マゼンタ文字
    'font_cyan': '\3[36m',  # シアン文字
    'font_white': '\3[37m',  # 白文字
    'font_select': '\3[38;2;103;65;150m',  # 文字色指定(R, G, B=103, 65, 150)

    'bg_black': '\3[40m',  # 黒背景
    'bg_red': '\3[41m',  # 赤背景
    'bg_green': '\3[42m',  # 緑背景
    'bg_yellow': '\3[43m',  # 黄背景
    'bg_blue': '\3[44m',  # 青背景
    'bg_magenta': '\3[45m',  # マゼンタ背景
    'bg_cyan': '\3[46m',  # シアン背景
    'bg_white': '\3[47m',  # 白背景
    'bg_select': '\3[48;2;103;65;150m',  # 背景色選択(R, G, B=103, 65, 150)

    'fonte_black': '\3[90m',  # 黒の強調文字
    'fonte_red': '\3[91m',  # 赤の強調文字
    'fonte_green': '\3[92m',  # 緑の強調文字
    'fonte_yellow': '\3[93m',  # 黄の強調文字
    'fonte_blue': '\3[94m',  # 青の強調文字
    'fonte_magenta': '\3[95m',  # マゼンタの強調文字
    'fonte_cyan': '\3[96m',  # シアンの強調文字
    'fonte_white': '\3[97m',  # 白の強調文字

    'bge_black': '\3[100m',  # 黒の強調背景
    'bge_red': '\3[101m',  # 赤の強調背景
    'bge_green': '\3[102m',  # 緑の強調背景
    'bge_yellow': '\3[103m',  # 黄の強調背景
    'bge_blue': '\3[104m',  # 青の強調背景
    'bge_magenta': '\3[105m',  # マゼンタの強調背景
    'bge_cyan': '\3[106m',  # シアンの強調背景
    'bge_white': '\3[107m',  # 白の強調背景
}

# リセット用のdict
resets = {
    'font_default': '\3[39m',  # 文字色をデフォルトに
    'bg_default': '\3[49m',  # 背景色をデフォルトに
    'all_reset': '\3[0m',  # 全てリセット
}

大きく分けると、書式・文字色・背景色・文字色強調・背景色強調の5つの分類ができる。これらの書式や色を以下のコードで体裁を整えつつ出力する。

注意点として、色を指定したらリセットをかけないといけない点。リセットしないとどうなるかは次の章で解説する。

# escapesのkeysで一番長い文字数
max_len_e = len(max(escapes, key=len))
print(max_len_e)
# 13

# 各エスケープシーケンスを出力
for name, escape in escapes.items():
    # 色をつけるコードの数値部分だけを抽出
    # 先読みと後読みで「[」と「;」(;はなくてもいい)の間の数値だけを選択
    code = re.findall(r'(?<=\\[)\\d+(?=;*)', escape)[0]

    # 各文字が揃うように右寄せや左寄せを使う
    reset = resets['all_reset']
    out = f"{code: >4}, {name: <{max_len_e}}: {escape}文字{reset}"
    print(out)

f文字で: >4とすることで、文字を4文字分確保しつつ、足りなかったら半角スペースで補って右寄せとなる。name: <{max_len_e}も同じで、各項目の名称の最長名称がに合わせて左寄せにしている。

上記以外の数字、例えば[22mなどを選択しても何も起きなかった。もしかしたら役割があるかも知れないが確認できないのでここではスルーしておく。ほとんど対応していないとかいう数字もあるっぽい。

文字色と背景色を組み合わせる

文字色と背景色の組み合わせも可能。また、色指定後のリセットをかけないと、次の文字列で色を指定していない部分が侵食されてしまう。今回だと3行目の黄背景が4行目に侵食している。

さらに、背景だけ元に戻すと書式や文字の色はそのまま保持される。それが最後の赤文字だ。ターミナル上ではitalicが反映されていないが、以下のVScodeの統合ターミナルだとうまくいっている。

termcolorでサクッと作成

ここまでエスケープシーケンスを直接指定して色を変更したが、如何せん文字がゴチャゴチャして見づらい。楽に記述できないかとなるがそんな時に使えるのがtermcolor

例えば上の出力だと以下のようにコードを書けばできる。かなり簡単。

import termcolor

print('文字')
print(termcolor.colored(text='文字', color='green', on_color=None, attrs=None))

colorが文字色、on_colorが背景色、attrsが書式で配列で指定。さっきのゴチャゴチャ感と比べるとだいぶと簡単に描くことができる。

使用できる色と書式

使用できる色はエスケープシーケンスの時と同様、というより、いちいちエスケープシーケンスを書かなくても勝手にtermcolorが処理してくれるっていう使用っぽい。

以下のコードで出力した出力結果が上の画像だけど、これをCode Runnerの出力部分で見るとエスケープシーケンスが出力されている。ということでtermcolor勝手に色々してくれて楽な存在。

colors = ['grey', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']

on_colors = [f"on_{color}" for color in colors]

# reverseは文字色と背景色を入れ替え、concealedは文字を隠す
fonts = ['bold', 'dark', 'underline', 'blink', 'reverse', 'concealed', ]

colors_tpl = ('red', 'yellow', 'cyan', 'blue')
on_colors_tpl = ('on_white', 'on_blue', 'on_yellow', 'on_magenta')
font_tpl = ('bold', 'dark', 'reverse', 'concealed')

for color, on_color, attrs in zip(colors_tpl, on_colors_tpl, font_tpl):
    kwargs = dict(color=color, on_color=on_color, attrs=[attrs])
    out = termcolor.colored('文字', **kwargs)
    print(out)
# [1m[47m[31m文字[0m
# [2m[44m[33m文字[0m
# [7m[43m[36m文字[0m
# [8m[45m[34m文字[0m

文字色と背景色の一覧表を作成

最後に、文字色と背景色の色具合を見るための表を作成。どの色の組み合わせだと見づらいのかは実際に作ってみないと分からないとなると面倒。なので予め作成しておいた。

色んなサイトで既にこのような表はあるが、執筆者もこれに乗って作成する。縦の列が背景色を、横の行が文字色を表している。

print('ANSI', end='')  # 左上のANSIの文字
for bg in bgs:
    bg_code = re.findall(r'(?<=\\[)\\d+(?=;*)', bgs[bg])[0]
    print(f"{bg_code: >4}", end=' ')  # 背景色を表すヘッダ部分
print()  # 改行

for font in fonts:
    font_code = re.findall(r'(?<=\\[)\\d+(?=;*)', fonts[font])[0]
    print(font_code, end=': ')  # 文字色を表すインデックス部分
    for bg in bgs:
        # 各色の組み合わせ
        print(fonts[font] + bgs[bg] + 'moji' + '\3[0m', end=' ')
    print()  # 改行

f文字でいい感じの位置にANSIのカラーコードを書くという点とprinttendで改行しないように調節している点がキモ。いい感じにレイアウトしようとした結果、こうなった。

可読性を上げる簡単な方法

今回はANSIのカラーコードを使用してターミナルの出力文に色をつける方法について解説した。執筆者としてはあまり使用する機会がなかったが、初心者にここが重要とか教えるときに良さそうと感じた。

あとは予めどこかで関数化して、「.BLACK」とかで取り出すClassの方法を使う例が多く見られた。執筆者はClassについてはまだまだ勉強できていないので紹介しなかったが、使えるなら便利そう。

インパクトは大きいのでびっくりさせたい時にも使えそう。

関連記事

【plotly&buttons】ボタンを押すとプロット・背景の色が変わる変なグラフ

続きを見る

【plotly&heatmap】go.Heatmapで2次元配列をマップ化

続きを見る

【python&表出力】tabulateモジュールで出力を表形式にする

続きを見る

【python&~】数値にチルダ(~)をつけると値が+1されて負の数になる(ビット反転)

続きを見る

関連コンテンツ

スポンサーリンク

Amazonのお買い物で損したない人へ

1回のチャージ金額通常会員プライム会員
¥90,000〜2.0%2.5%
¥40,000〜1.5%2.0%
¥20,000〜1.0%1.5%
¥5,000〜0.5%1.0%

Amazonギフト券にチャージすることでお得にお買い物できる。通常のAmazon会員なら最大2.0%、プライム会員なら2.5%還元なのでバカにならない。

ゲットしたポイントは通常のAmazonでのお買い物に使えるからお得だ。一度チャージしてしまえば、好きなタイミングでお買いものできる。

なお、有効期限は10年だから安心だ。いつでも気軽にAmazonでお買い物できる。

Amazonチャージはここから出来るで

もっとお得なAmazon Prime会員はこちらから

30日間無料登録

執筆者も便利に使わせてもらってる

スポンサーリンク

  • この記事を書いた人

メガネ

独学でpythonを学び天文学系の大学院を修了。 ガジェット好きでMac×Android使い。色んなスマホやイヤホンを購入したいけどお金がなさすぎて困窮中。 元々、人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂ったために今も人生めちゃくちゃ楽しい。 pythonとガジェットをメインにブログを書いていますので、興味を持たれましたらちょこちょこ訪問してくだされば幸いです🥰。 自己紹介→変わって楽しいの繰り返し

-Python基礎
-