このページでは、printf
で「文字の色や背景色を変更する」方法について解説します。
これにより、下の図のように printf
で出力した文字の色や背景色を変化させ、例えばエラーであることや警告であることを分かりやすくしたり、色を利用することで作成できるプログラムの幅を広げることもできます。
といっても、この文字の色や背景色の変更は、C言語の printf
そのものの機能ではなく、printf
の出力先、例えばターミナルやコマンドプロント等における機能になります(このページでは、printf
の出力先がターミナルであることを前提に解説していきます)。
具体的には、文字の色や背景色を変更するためにC言語のソースコードで行うことは、printf
でのエスケープシーケンス(命令みたいなもの)の出力になります。これにより、以降の printf
での文字列の出力時に、ターミナルが事前に出力されたエスケープシーケンスに応じて文字の色や背景色を変更してくれます。
色を変更してくれるのはターミナルですので、printf
の出力先となるターミナルが色の変更に対応していなかったり、指定するエスケープシーケンスに対応していない場合などは、色の変更ができないので注意してください。
ちなみに、改行を行う際に利用する \n
もエスケープシーケンスの1つになります。エスケープシーケンス自体については下記ページで解説していますので、エスケープシーケンスについて知りたい方はこちらを参考にしていただければと思います。
また、私は Mac を使用しており、Mac のターミナルを利用して動作確認を行なっています。おそらく Linux のターミナルでも同様の方法で色の変更を行うことができると思いますが、Windows のコマンドプロンプト等は対応していない可能性もあるので注意してください(対応しているかどうかは、実際に以降で紹介するソースコードのプログラムを実行してみれば確認できます)。
Contents
printf
で文字の色や背景色を変更する方法
前述の通り、printf
で文字の色や背景色を変更する際にはエスケープシーケンスを利用します。
具体的にどのようなエスケープシーケンスを利用するのか確認していきましょう!
文字の色や背景色を変更するエスケープシーケンス
文字の色や背景色を変更する際には、printf
で下の図のようなエスケープシーケンスを出力します。
<code>
の部分で、どのように色を変更するかを指定します。printf
で上記エスケープシーケンスを出力すると、それ以降ターミナルに表示される文字の色や背景色が、出力されたエスケープシーケンスに応じて変化することになります。
また、\x1b
の部分は \033
と \e
に置き換えても良いようです。このページでは \x1b
で統一して解説していきたいと思います。
例えば、<code>
に 31
を指定して上記エスケープシーケンスを出力し、その後てきとうな文字列を出力してみましょう!<code>
に 31
を指定することで、文字の色を赤に変更することができるはずです。
これは、例えば下記のようにソースコードを記述することで実現することができます。
#include <stdio.h>
int main(void) {
printf("Hello World!");
printf("\x1b[31m");
printf("Hello!\n");
printf("Good Bye\n");
printf("Good Morning\n");
}
上記のソースコードをコンパイルして実行してみてください。文字の色が変化しているでしょうか?
私の場合は下の図のように文字列が出力されました。文字の色が途中から赤に変化していることが確認できると思います。
スポンサーリンク
色の変更のリセット
上の図の結果を見ても分かるとおり、色が変化するのは \x1b[<code>m
の出力以降になります。逆に、\x1b[<code>m
の出力以降はずっと色が変化していることになります(おそらくプログラム終了まで)。
ただ、\x1b[<code>m
以降ずっと同じ色になってしまうのも不便ですよね…。
これを解決するために、下記のようなリセットを行うためのエスケープシーケンスも用意されています。
\x1b[0m
:全ての変更を元に戻す\x1b[39m
:文字の色を元に戻す\x1b[49m
:背景色を元に戻す\x1b[m
:全ての変更を元に戻す(?)
\x1b[<code>m
を出力して色を変更した後でも、\x1b[0m
等でリセットを行うことで色の変更を取り消すことができます。
したがって、下記のようにソースコードを記述すれば、Hello!
のみ文字の色が赤に変化し、それ以外は文字の色が元のままの状態で出力されることになります。
#include <stdio.h>
int main(void) {
printf("Hello World!");
printf("\x1b[31m");
printf("Hello!\n");
printf("\x1b[0m");
printf("Good Bye\n");
printf("Good Morning\n");
}
実際に私が試した結果は下の図になります。
色を変更する際には、色を変更するエスケープシーケンス \x1b[<code>m
の出力だけでなく、リセットを行う出力も重要になりますので、その点はご注意ください。特に \x1b[0m
は覚えておくと良いと思います。
また、前述の通り、色が変化するのは \x1b[<code>m
の出力以降です。ですので、わざわざ上記のソースコードのように \x1b[31m
等のエスケープシーケンスと通常の文字列の出力を別の printf
で行う必要もなく、1つの printf
の中でエスケープシーケンスによる色の変更と文字列出力を行うこともできます。
#include <stdio.h>
int main(void) {
printf("Hello World!");
printf("\x1b[31mHello!\n\x1b[0m");
printf("Good Bye\n");
printf("Good Morning\n");
}
"\x1b[31mHello!\n\x1b[0m"
がちょっとややこしいですが、下の図のように分けて考えると、各部がどのような役割を果たしているかが分かりやすくなるかと思います。
<code>
を複数同時に指定する方法
ここまでの例では <code>
を1つのみ指定してきましたが、同時に複数指定することも可能です。これにより、例えば文字の色と背景色を同時に変更するようなことができます。
<code>
を複数指定する場合、<code>
を ;
区切りで指定します。例えば下記は、<code>
を3つ同時に指定する例になります。
\x1b[<code>;<code>;<code>m
例えば、<code>
として 31
と 42
を同時に指定する場合は下記のように記述をして printf
で出力を行います。31
は文字の色を赤に変更する数値であり、42
は背景色を緑に変更する数値になります。
printf("Hello!\x1b[31;42mHello!\x1b[0mHello!\n");
私の場合、実行結果は下の図のようになりました。文字の色と背景色を同時に変更することができていることが確認できると思います。
<code>
に指定可能な数値の調べ方
ここまで <code>
には 31
や 42
などを指定してきましたが、他にも指定可能な数値はたくさん存在します。
<code>
に指定可能な代表的な数値を下記で示しておきます。
31
:文字の色を赤に変更32
:文字の色を緑に変更33
:文字の色を黄に変更34
:文字の色を青に変更35
:文字の色をマゼンタに変更36
:文字の色をシアンに変更37
:文字の色をグレーに変更41
:背景の色を赤に変更42
:背景の色を緑に変更43
:背景の色を黄に変更44
:背景の色を青に変更45
:背景の色をマゼンタに変更46
:背景の色をシアンに変更47
:背景の色をグレーに変更
ただ、使用しているターミナルによって対応する <code>
や、その <code>
を指定した時の出力結果(色味など)が異なります。なので、<code>
に何を指定すれば出力がどのように変化するかを明確に伝えるのが難しいです。
そのため、<code>
に指定可能な値や <code>
に指定した値によってどのように出力が変化するかについては、あなた自身で確認してみていただくのが一番良いと思います。
その方法は簡単で、<code>
に指定可能な値の全パターンを実際に試し、その出力結果を確認してみれば良いだけです。
そしてこれは、下記のソースコードを実行することで実現することができます。
#include <stdio.h>
int main(void) {
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 10; j++) {
printf("\x1b[%dm %03d \x1b[0m", i * 10 + j, i * 10 + j);
}
printf("\n");
}
}
上記では、\x1b[<code>m
の <code>
部分に 0
〜 119
までの値を当てはめながら色等の変更を行なっています。そして、変更後に <code>
に指定した値を3桁で表示しています(おそらく 0
〜 119
のみで足りるはず)。
わざわざ2重ループにしているのは、出力結果を綺麗に並べるためです(内側のループが終了するたびに改行するようにしている)。
上記ソースコードをコンパイルして実行すれば、下の図のように <code>
に全パターンを指定した結果が並べて表示されます。
出力されている数値が、その数値を出力するときに指定した <code>
になります。ですので、例えば下の図の箇所を確認すれば、文字の色を緑に変更するためには <code>
に 32
を指定すれば良いことが確認できます(先頭の 0
の指定は不要です)。
したがって、文字の色を緑に変更したいのであれば、文字列を出力する前に \x1b[32m
を出力してやれば良いことになります。
printf("\x1b[32mHello!\n");
また、実際に全パターンの表示を行うと確認できると思いますが、実はエスケープシーケンスでは文字の色や背景色だけでなく、斜体設定や下線設定なども行うことができます(なぜか 001
が表示されていないが、1
は太字設定のはず)。
例えば下記のように printf
を実行すれば、Hello!
がマゼンタ色かつ、太字&下線付きで出力されることになります。
printf("\x1b[35;1;4mHello!\n");
こんな感じで、<code>
の全パターンを実際に試してやれば、自身が望む文字の出力形式にするために <code>
に何を指定すれば良いかを簡単に確認することができます。
ただし、前述の通り出力先によって見え方が異なる場合があるので注意してください。例えば、私は Mac 標準のターミナルアプリと VSCode 付属のターミナルの両方で出力結果を試してみましたが、この2つでも色味などが結構異なりました。
実際の両者の出力結果は下記のようになります。小さくて分かりくいかもしれませんが、044
のあたりは両者の違いが分かりやすいのではないかと思います。
こんな感じで、同じソースコードであったとしても、使用するターミナルによっては出力結果が異なる可能性があるので注意してください。
スポンサーリンク
もっと細かく色の指定を行う方法
使用するターミナルによっては、もっと細かく色の指定を行うこともできます。
細かく色を指定して色の変更を行う際には、エスケープシーケンスとして \x1b[38;5;<color_number>m
と \x1b[48;5;<color_number>m
の出力を行います。
前者では文字の色の変更、後者では背景の色の変更を行うことができます。また、<color_number>
には 0
〜 255
の色番号を指定します。
色番号に応じた色がどのように変化するかについては、<code> に指定可能な数値の調べ方 で紹介した方法と同様に、実際に出力を行なってみると分かりやすいと思います。
下記は、\x1b[38;5;<color_number>m
の <color_number>
部分を 0
〜 255
の間で変化させながら文字を出力するソースコードになります。
#include <stdio.h>
int main(void) {
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) {
printf("\x1b[38;5;%dm %03d \x1b[0m", i * 16 + j, i * 16 + j);
}
printf("\n");
}
}
私の環境での実行結果は下の図のようになりました。
さらに下記は、\x1b[48;5;<color_number>m
の <color_number>
部分を 0
〜 255
の間で変化させながら文字を出力するソースコードになります。
#include <stdio.h>
int main(void) {
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) {
printf("\x1b[48;5;%dm %03d \x1b[0m", i * 16 + j, i * 16 + j);
}
printf("\n");
}
}
私の環境での実行結果は下の図のようになりました。
<code> に指定可能な数値の調べ方 で紹介した出力結果に比べると、表現できる色の幅が広がっていることが確認できると思います。
ただし、前述の通り使用するターミナルによって \x1b[38;5;<color_number>m
や \x1b[48;5;<color_number>m
のエスケープシーケンスに対応していない可能性があるので注意してください。
さて、上記で <color_number>
に指定できるのは 0
〜 255
のみでしたので、指定できる色の種類は最大 256
であることになります。
それに対して、使用するターミナルによっては、さらにもっと多くの種類の色を指定することも可能です。
具体的には、\x1b[38;2;<r>;<g>;<b>m
や \x1b[48;2;<r>;<g>;<b>m
のエスケープシーケンスを出力することで、前者では文字の色、後者では背景の色を <r>
と <g>
と <b>
に応じた色に変更することができます。<r>
と <g>
と <b>
には、それぞれ赤色・緑色・青色の輝度値を指定します。
また、<r>
と <g>
と <b>
それぞれに対して 0
〜 255
の値を指定することができます。なので、256
x 256
x 256
種類の色を表現することができることになります。
輝度値と聞くと難しいかもしれませんが、要は各色の強さを表す値になります。画像の各画素の色も赤色・緑色・青色の輝度値で表現されていたりします。
例えば \x1b[38;2;255;0;0m
を出力すれば、以降の文字の色が赤色になりますし、\x1b[48;2;255;255;0m
を出力すれば、以降の背景の色が黄色になります。
また、例えば画像を読み込み、画像の各画素の色に合わせて printf
での出力文字の色を変更するようにすれば、下の図のような文字列をターミナル上に出力するようなこともできます(出力しているのは ■
の文字のみで、画像の各画素の色に合わせて ■
の色を変更するようにしています)。
ただ、上の図の出力結果は VSCode 付属のターミナルのものであり、同じプログラムを使用しても、Mac 標準のターミナルの場合は下の図のように表示されてしまいました…。
要は、Mac の標準ターミナルでは \x1b[38;2;<r>;<g>;<b>m
のエスケープシーケンスに対応していないということです。なので出力結果がデタラメなものになってしまいます。
この形式の色指定は、輝度値やカラーコードの扱いに慣れている方には一番馴染み深い指定方法になると思いますが、残念ながら、少なくとも Mac の標準ターミナルでは対応していないようです(もしかしたらもっと他に良いエスケープシーケンスがあるのかもしれないですが…)。
こんな感じで、使用するターミナルによってはエスケープシーケンスに対応していないものがありますので注意してください。
参考サイト
このページを作成するにあたり、下記のページを参考にさせていただきました。
- http://programcode.blog.fc2.com/blog-entry-6.html
- https://misc.flogisoft.com/bash/tip_colors_and_formatting
まとめ
このページでは、printf
で「文字の色や背景色を変更する」方法について解説しました!
printf
で \x1b[<code>m
のエスケープシーケンスを出力することで、それ以降の出力結果の色等を変更することができます。例えば <code>
に 31
を指定すれば、文字の色を赤に変更できます。
具体的に <code>
に指定可能な値や、その値によって出力結果がどのように変化するかは <code> に指定可能な数値の調べ方 で紹介した方法で実際に調べてみていただければと思います。
また、プログラムの途中で色を元に戻したい場合はリセットを行う必要があります。リセットは、例えば \x1b[0m
のエスケープシーケンスを出力することで行うことができます。
また、使用するターミナルによっては もっと細かく色の指定を行う方法 で紹介した下記のエスケープシーケンスを指定することで、色をもっと細かく指定することも可能です。
\x1b[38;5;<color_number>m
\x1b[48;5;<color_number>m
\x1b[38;2;<r>;<g>;<b>m
\x1b[48;2;<r>;<g>;<b>m
ただし、ターミナルによってはエスケープシーケンスが対応していない、色合いが異なるなどの違いも発生するので注意してください。
とりあえず printf
で文字の色や背景の色を変更できれば実現できるプログラムの幅も広がりますので、ぜひ利用してみてください!
オススメの参考書(PR)
C言語学習中だけど分からないことが多くて挫折しそう...という方には、下記の「スッキリわかるC言語入門」がオススメです!
まず学習を進める上で、参考書は2冊持っておくことをオススメします。この理由は下記の2つです。
- 参考書によって、解説の仕方は異なる
- 読み手によって、理解しやすい解説の仕方は異なる
ある人の説明聞いても理解できなかったけど、他の人からちょっと違った観点での説明を聞いて「あー、そういうことね!」って簡単に理解できた経験をお持ちの方も多いのではないでしょうか?
それと同じで、1冊の参考書を読んで理解できない事も、他の参考書とは異なる内容の解説を読むことで理解できる可能性があります。
なので、参考書は2冊持っておいた方が学習時に挫折しにくいというのが私の考えです。
特に上記の「スッキリわかるC言語入門」は、他の参考書とは違った切り口での解説が豊富で、他の参考書で理解できなかった内容に対して違った観点での解説を読むことができ、オススメです。題名の通り「なぜそうなるのか?」がスッキリ理解できるような解説内容にもなっており、C言語入門書としてもかなり分かりやすい参考書だと思います。
もちろんネット等でも色んな観点からの解説を読むことが出来ますので、分からない点は別の人・別の参考書の解説を読んで解決していきましょう!もちろん私のサイトも参考にしていただけると嬉しいです!
入門用のオススメ参考書は下記ページでも紹介していますので、こちらも是非参考にしていただければと思います。
https://daeudaeu.com/c_reference_book/