【C言語】エスケープシーケンスを使ってみる
コンテンツ
はじめに
コマンドラインなどで操作しているとたまに文字の色が違う出力がでたりします。そのうち知るだろうと思って大して調べたりしていなかったのですが、最近その方法を知ったのでその備忘録です。
エスケープシーケンスの備忘録
エスケープシーケンスとは以下のような特殊な文字列です。
エスケープシーケンスとは、画面上に文字を出力する際に、文字そのものを出力するのではなく、文字色の変更やカーソルの移動、文字の消去など、文字出力の制御を行う特殊な文字列のことである。
引用 : weblio
「\n」や「\t」などはよく使いますが、それ以外にも上記にあるように文字色の変更・背景色の変更・カーソルの移動等もできます。例えば次のようなエスケープシーケンスがあります。
文字色変更
文字列 | 内容 |
---|---|
ESC[30m | 文字が黒色になる |
ESC[31m | 文字が赤色になる |
ESC[32m | 文字が緑色になる |
ESC[33m | 文字が黄色になる |
ESC[34m | 文字が青色になる |
ESC[35m | 文字がマゼンタになる |
ESC[36m | 文字がシアンになる |
ESC[37m | 文字が白色になる |
背景色変更
文字列 | 内容 |
---|---|
ESC[40m | (文字の)背景が黒色になる |
ESC[41m | 背景が赤色になる |
ESC[42m | 背景が緑色になる |
ESC[43m | 背景が黄色になる |
ESC[44m | 背景が青色になる |
ESC[45m | 背景がマゼンタになる |
ESC[46m | 背景がシアンになる |
ESC[47m | 背景が白色になる |
カーソル操作
nには数値が入ります。
文字列 | 内容 |
---|---|
ESC[nA | カーソルを上にn移動させる |
ESC[nB | カーソルを下にn移動させる |
ESC[nC | カーソルを右にn移動させる |
ESC[nD | カーソルを左にn移動させる |
ESC[nE | カーソルをn行下の先頭に移動させる |
ESC[nF | カーソルをn行下の先頭に移動させる |
画面消去
文字列 | 内容 |
---|---|
ESC[0J | カーソルより後ろを消去 |
ESC[1J | カーソルより前を消去 |
ESC[2J | 画面全体を消去 |
ESC[0K | カーソルの行のカーソルより後ろを削除 |
ESC[1K | カーソルの行のカーソルより前を消去 |
ESC[2K | カーソルの行全体を消去 |
「ESC」は「\e」「\033」「\x1b」と書きます。「\e」と「\033」が多いように感じます。
誰がエスケープシーケンスを解釈しているか
エスケープシーケンスで文字の色の変更、カーソルの操作などができるのは理解できたが、このエスケープシーケンスの仕様などはどこに載っているかが分からなかったので調べました。調べると、Tera Termとういう端末エミュレータのマニュアルに載っていました。またxtermとう端末エミュレータがサポートするプロトコルに「ANSI X3.64」の記載がありそこにも載っていました。現在は端末もソフトウェアのことがほとんどだと思うので誰がエスケープシーケンスを解釈しているか分からりずらいですが、文字の色の変更やカーソルの操作は端末が行っているようです(あたりまえか…)。エスケープシーケンスは端末によって解釈されるため、エスケープシーケンスは端末によって異なる(端末によっては使えない機能もある)。
参考
エスケープシーケンスで文字に色を付ける・プログレスバーを作る
コンソールでのプログラム実行時に文字色を変える方法を知った時にいろいろ調べてみると、エスケープシーケンスは他にもいろいろできることが分かったのですこしプログラムを作ってみました。
1. 進捗表示
何かの進捗を表示するプログラムです。コードは以下の通りです。const char str[] = "\e[1F--- %3d/100 ---\n";
で表示する文字列を格納しています。「\e[1F」で1行上にカーソルを移動させています。そうすることで同じ個所でカウントアップするような表示になります。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(){ int i; const char str[] = "\e[1F--- %3d/100 ---\n"; printf("+---progress---+\n\n"); for (i = 0; i < 101; i++){ printf(str, i); usleep(100000); } printf("\n"); return 0; }
2. 文字色を変えてローディング表示
文字色を赤色にして、ゲームのローディング画面のように「Now Loading…」を一文字ずつ表示し続けるプログラムです。行の削除、カーソルを行頭に移動する、文字列を赤色にするエスケープシーケンスを用いています。 このプログラムでは、制御シーケンス毎に変数( const char deleteLine[] = "\e[2K"; const char cousorToHeadOfLine[] = "\e[0G"; const char colorRed[] = "\e[31m";
)に変数に格納して必要な時に出力します。printf()
関数でなくwrite()
関数なのは、printf()
関数のバッファリングで、改行等がない文字列はバッファされてから出力されることがあるためである。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(){ int i, j, idx; const char str[] = "Now Loading..."; const char deleteLine[] = "\e[2K"; const char cousorToHeadOfLine[] = "\e[0G"; const char colorRed[] = "\e[31m"; write(fileno(stdout), colorRed, sizeof(colorRed)); for (i = 0; i < 1000; i++){ if (i > 0 && (i % strlen(str) == 0)){ write(fileno(stdout), deleteLine, sizeof(deleteLine)); write(fileno(stdout), cousorToHeadOfLine, sizeof(cousorToHeadOfLine)); } idx = i % strlen(str); write(fileno(stdout), &str[idx], 1); usleep(100000); } printf("\n"); return 0; }
3. プログレスバーの表示
プログレスバーを表示するプログラムです。2と同様に、カーソル操作などのエスケープシーケンスはそれぞれを変数に格納して必要なところでwrite()
関数で出力します。何度もエスケープシーケンスするときは、一度出力をまとめた方がOSにとっては割り込みがすくなくなってよさそうな改善点などが残っています。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(){ int i; const int N = 19; // progress count const char progressBar[] = "0 50 100\n+---------+---------+\n|...................|\n+-------------------+\n"; const char deleteLine[] = "\e[2K"; const char cousorToHeadOfLine[] = "\e[0G"; const char cousorMoveRight[] = "\e[1C"; const char cousorMoveDown[] = "\e[1B"; const char cousorUpOneLine[] = "\e[1F"; printf(progressBar); write(fileno(stdout), cousorUpOneLine, sizeof(cousorUpOneLine)); write(fileno(stdout), cousorUpOneLine, sizeof(cousorUpOneLine)); write(fileno(stdout), cousorMoveRight, sizeof(cousorMoveRight)); for (i = 0; i < N; i++){ write(fileno(stdout), "#", 1); sleep(1); } write(fileno(stdout), cousorMoveDown, sizeof(cousorMoveDown)); write(fileno(stdout), cousorMoveDown, sizeof(cousorMoveDown)); write(fileno(stdout), cousorToHeadOfLine, sizeof(cousorToHeadOfLine)); return 0; }
※文字色が変わっているのは2のプログラムを実施した後に実行しているから…