【C言語】エスケープシーケンスを使ってみる

2022年5月28日C言語,TIPS

コンテンツ

はじめに

コマンドラインなどで操作しているとたまに文字の色が違う出力がでたりします。そのうち知るだろうと思って大して調べたりしていなかったのですが、最近その方法を知ったのでその備忘録です。

エスケープシーケンスの備忘録

エスケープシーケンスとは以下のような特殊な文字列です。

エスケープシーケンスとは、画面上に文字を出力する際に、文字そのものを出力するのではなく、文字色の変更やカーソルの移動、文字の消去など、文字出力の制御を行う特殊な文字列のことである。

引用 : 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;
        }


文字を赤色にしてNowLoadingを表示するプログラム

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;
        }


文字を赤色にしてNowLoadingを表示するプログラム

※文字色が変わっているのは2のプログラムを実施した後に実行しているから…

C言語

Posted by 3流PG