以前 ¥n はエスケープシーケンスで動作を指定するモノで、この指定は改行になるという説明をしました。このエスケープシーケンスは他にもいろいろあるのですが、ここまで表示が味気ないので、今回は色などの装飾を付けてみたいと思います。
※ ちょっと横道に逸れます汗
--
いきなりここに飛んで来ちゃった人は、よろしければ下記からご覧ください。
C言語基礎講座インデックス


  • エスケープシーケンスの種類
¥n 改行以外で代表的なエススープコードは以下の通りです。

¥a BEEP音を鳴らす
¥b カーソルを1文字戻す
¥n LF 改行(次の行頭に戻る)
¥r CR 改行(行頭に戻る)
¥t タブ文字
¥0 NULL文字

¥n は改行と説明していますが、実はシステムによって違いがあります。Windows 環境が特殊な部類に入るのですが、普及台数が多いため無視できない状態となっており、現状で ¥n だけで改行という解釈となっています。この辺りはなかなか厄介に状態になっているので、興味があれば 改行コード CR LF CRLF で検索してみると良いでしょう。この講座では VS2022 を前提としていますので、改行は ¥n だけとしています。

¥0 は NULL 文字です。NULLは「ヌル」と発音したり「ナル」と発音したりします※1。文字列の最後尾等に指定される文字で、数値的には 0x00 です※2。私は Windows C言語環境下では「ヌル」と呼ぶ事にしています。これをきっちり説明するためには、配列とポインタの説明が必要になりますので、今回は説明を省略します。

■ 今は覚えなくても良いTips
※1. 英語の発音的には「ナル」が近いです。
※2. NULL 文字列の場合は 0x00(char型)ですが、ヌルポインタではシステム依存でサイズが変わります。

簡単な動作テストとして下記のプログラムを実行してみてください。

#include <stdio.h> void main() { printf("その他のエスケープシーケンス"); printf("\b"); printf("abc"); printf("\a"); printf("\n"); }

これの動作結果、想像できましたでしょうか?実行するとポロピン♪という音とともに以下のようになります。※ BEEP 音は環境依存なのであなたの環境では違う音かもしれません。
aが消えた?
はて? a が表示されていません。謎ですよね。こういうときはステップトレースで動作を1行ずつ見ていくと分かりやすいです。abc が表示されたタイミングで止めてみます。
スの途中からabcと表示された
なんと、スの途中からabcが表示されています。スは倍角文字といって、2バイトセットで文字が出来ています。¥b は1文字戻るとありますが、実際には1バイトしか戻りませんので、倍角文字の途中にカーソルが移動します。その位置から abc と書いてしまうのですが、それだとスという文字が文字として成立しないので、自動的に補正されてスに戻るため、a が上書きされてしまうと言うワケなのです。

文字列は実はかなーり奥が深いので、説明し始めるとキリがありません。C言語講座ではサラっと流してしまいますが、ある程度理解が進んだ時に、一度は見ておくと良いかもしれません。説明もどうしても難解になるので、文字列は中級から上級に位置する処理ですね…
※ 革靴とかでいちいち靴紐締めるの面倒ですよね・これ使うとスパッと履けます。ただ、1年ぐらいで切れそうになりますが安いので消耗品と考えるべきかなと。

  • 文字に色を付ける
色を付けるエスケープシーケンスは例えば以下のように記述します。

¥x1b[??m
¥x1b は、エスケープコードです。16進数で直接文字コードを指定しています。 [ で続く文字が引数(動作を指定する値)である事を意味しています。そして ??m が色コードです。勘の良い人ならもしかして ??m 以外もあるのかなと思ったかもしれませんが、その通りで色を付ける以外にいくつかの機能があります。代表的な引数を記述してみます。

2J 画面を消す
??A カーソルを上に ?? 回移動
??B カーソルを下に ?? 回移動
??C カーソルを右に ?? 回移動
??D カーソルを左に ?? 回移動
?;?H カーソルを ?,? の位置に移動
30m 文字を黒色にする
31m 文字を赤色にする
32m 文字を緑色にする
33m 文字を黄色にする
34m 文字を青色にする
35m 文字を紫色にする
36m 文字を水色にする
37m 文字を白色にする
40m 背景を黒色にする
41m 背景を赤色にする
42m 背景を緑色にする
43m 背景を黄色にする
44m 背景を青色にする
45m 背景を紫色にする
46m 背景を水色にする
47m 背景を灰色にする
0m 属性クリア

例えば、こんなプログラムを記述して…

#include <stdio.h> void main() { printf("\x1b[5;10H"); printf("\x1b[43m"); printf("\x1b[31m"); printf("C言語ちょっとだけ理解し始めた!\n"); printf("\x1b[0m"); }

このプログラムを実行するとこんな表示結果が得られます。
色付きで表示された
実行結果を見て「あれ?」って思いませんか。カーソル位置の移動ですが X,Y の指定ではなく、Y,X と指定しています。感覚的には反対ですよね。これ、間違えやすいです。あと、色味に少し癖があるのと、Windows と Linux 等のような環境の違いでも、微妙に差異が生じるので注意が必要です。


  • 別名定義
プログラムリストの中で # から始まる行を見た事ある人はいませんでしょうか。これはプリプロセッサと言って、直接的なプログラム処理ではなく、前処理的なプログラム生成をサポートするためのコンパイラ依存制御命令です。コンパイラ依存と言っても、ある程度は互換性があります。

¥x1b[31m  とか書かれていても全く分かりづらいですよね。そこで別名定義を教えてしまいます。それが #define です。使い方は簡単で例えば文字を赤色にするエスケープシーケンスを FRED と別名にしたい場合は、

#define FRED "\x1b[31m"
このように記述します。この FRED をどう使うかですが、一番簡単な使い方としては、

printf(FRED);

と記述します。これだと使いにくい場合は、文字列を表示する書式文字列 %s を使うと

printf("%s赤色\n", FRED);

…のような書き方が出来ます。と言う事で、代表的なエスケープコードを全て別名定義してしまいましょう。

#define BEEP "\a" // BEEP音を鳴らす #define BS "\b" // カーソルを1文字戻す #define LF "\n" // LF 改行(次の行頭に戻る) #define CR "\r" // CR 改行(行頭に戻る) #define TAB "\t" // タブ文字 #define NULL 0 // NULL文字 #define SCRCLEAR "\x1b[2J" // 画面を消す #define CURUP "\x1b[%dA" // カーソルを上に移動 #define CURDOWN "\x1b[%dB" // カーソルを下に移動 #define CURRIGHT "\x1b[%dC" // カーソルを右に移動 #define CURLEFT "\x1b[%dD" // カーソルを左に移動 #define LOCATE "\x1b[%d;%dH" // カーソルを ?,? の位置に移動 #define FBLACK "\x1b[30m" // 文字を黒色にする #define FRED "\x1b[31m" // 文字を赤色にする #define FGREEN "\x1b[32m" // 文字を緑色にする #define FYELLOW "\x1b[33m" // 文字を黄色にする #define FBLUE "\x1b[34m" // 文字を青色にする #define FMAGENTA "\x1b[35m" // 文字を紫色にする #define FCYAN "\x1b[36m" // 文字を水色にする #define FWHITE "\x1b[37m" // 文字を白色にする #define BBLACK "\x1b[40m" // 背景を黒色にする #define BRED "\x1b[41m" // 背景を赤色にする #define BGREEN "\x1b[42m" // 背景を緑色にする #define BYELLOW "\x1b[43m" // 背景を黄色にする #define BBLUE "\x1b[44m" // 背景を青色にする #define BMAGENTA "\x1b[45m" // 背景を紫色にする #define BCYAN "\x1b[46m" // 背景を水色にする #define BDARK "\x1b[47m" // 背景を灰色にする #define ATBCLEAR "\x1b[0m" // 属性クリア

さて、上記の中でなんだこりゃという別名定義があるの、分かりますでしょうか? CUR から始まる定義と、LOCATE という定義です。文字列の中に %d という書式文字列があります。これ、どう使うか想像できますでしょうか?

例えば LOCATE は以下のように使います。

printf(LOCATE, 3, 10);
この書き方、なるほどって思いません?
この別名定義を使う事で、以下のような書き方が出来るようになります。

#include <stdio.h> #define BEEP "\a" // BEEP音を鳴らす #define BS "\b" // カーソルを1文字戻す #define LF "\n" // LF 改行(次の行頭に戻る) #define SCRCLEAR "\x1b[2J" // 画面を消す #define LOCATE "\x1b[%d;%dH" // カーソルを ?,? の位置に移動 #define FRED "\x1b[31m" // 文字を赤色にする #define ATBCLEAR "\x1b[0m" // 属性クリア void main() { printf(SCRCLEAR); printf("%s赤色\n", FRED); printf(LOCATE, 3, 10); printf("%sその他のエスケープシーケンス%s", ATBCLEAR, BS); printf("abc"); printf(BEEP); printf(LF); }

これでだいぶ画面レイアウトが自由自在に出来るようになりますよね?
※ 無線で繋がるPS2待望のコントローラ!

  • ファイル分割
この #define で定義したプログラムは大変見やすいのですが、プログラムを書くたびに、いちいち #define と書くのは面倒ですよね。そこで、この別名定義は別のファイルとして作成しておいて、それを main が記載されている場所に読み込んでしまいましょう。これは既になんとなく使っている #include を活用します。

別名ファイルの作成手順です。今回はファイルの名前を Define.h とします。.h とはヘッダーファイルに使われる拡張子です。ヘッダーファイルは、今回の別名定義のようなプログラムコードを含まない定義だけのファイルの事を指します。

ファイル分割と追加の仕方ですが、まず右側のソリューションエクスプローラーのプロジェクト名 Project1 を右クリック、追加、新しい項目を選択します。
新しい項目を選択
新しい項目の追加ダイアログが開きますので、ヘッダーファイルを選択します。名前を Define.h と書き換えて、追加ボタンを押します。
ヘッダーファイルを作る
無事、ヘッダーファイルフォルダ内に Define.h が追加されました。
Define.hが追加された
今までプログラム編集してきた Main.cpp タブの横にも Define.h が追加されていると思います。追加されていない場合は、ソリューションエクスプローラーから Define.h をダブルクリックしてください。Define.h を見てみると…おや、なにか最初から # で始まるプリプロセッサが追加されていますね?
自動的にプリプロセッサpragma onceが追加されている
この #pragma once は、同じファイルを二度読み込まないという動作指定のプリプロセッサです。あちこちでヘッダファイルを読み込んでいくと、そのうち同じファイルが二度も三度も読み込まれて衝突してラベル多重定義エラーになってしまう事があります。それを未然に防ぐのが #pragma once です。※ このプリプロセッサはもしかすると VS2022 固有かもしれません。

#pragma once の次から #define を記述します。

#pragma once #define BEEP "\a" // BEEP音を鳴らす #define BS "\b" // カーソルを1文字戻す #define LF "\n" // LF 改行(次の行頭に戻る) #define CR "\r" // CR 改行(行頭に戻る) #define TAB "\t" // タブ文字 #define NULL "\0" // NULL文字 #define SCRCLEAR "\x1b[2J" // 画面を消す #define CURUP "\x1b[%dA" // カーソルを上に移動 #define CURDOWN "\x1b[%dB" // カーソルを下に移動 #define CURRIGHT "\x1b[%dC" // カーソルを右に移動 #define CURLEFT "\x1b[%dD" // カーソルを左に移動 #define LOCATE "\x1b[%d;%dH" // カーソルを ?,? の位置に移動 #define FBLACK "\x1b[30m" // 文字を黒色にする #define FRED "\x1b[31m" // 文字を赤色にする #define FGREEN "\x1b[32m" // 文字を緑色にする #define FYELLOW "\x1b[33m" // 文字を黄色にする #define FBLUE "\x1b[34m" // 文字を青色にする #define FMAGENTA "\x1b[35m" // 文字を紫色にする #define FCYAN "\x1b[36m" // 文字を水色にする #define FWHITE "\x1b[37m" // 文字を白色にする #define BBLACK "\x1b[40m" // 背景を黒色にする #define BRED "\x1b[41m" // 背景を赤色にする #define BGREEN "\x1b[42m" // 背景を緑色にする #define BYELLOW "\x1b[43m" // 背景を黄色にする #define BBLUE "\x1b[44m" // 背景を青色にする #define BMAGENTA "\x1b[45m" // 背景を紫色にする #define BCYAN "\x1b[46m" // 背景を水色にする #define BDARK "\x1b[47m" // 背景を灰色にする #define ATBCLEAR "\x1b[0m" // 属性クリア
Main.cpp に戻ります。こちらで記述していた #define は全て削除します。当然、printf で使用されているエスケープシーケンスの別名定義部分がエラーになります。
未定義エラーが多発
赤色の波線で表示されているエラー箇所に、マウスカーソルを持って行くとエラーの詳細が表示されます。
エラーの詳細表示
ん?考えられる修正内容を表示するがリンクになってます。なんと #include "Define.h" を追加という選択肢が!?
修正方法の提案
これをマウスで左クリックすると、なんということでしょう!
includeが追加されたMain.cpp
#include <stdio.h> の下に自動的に #include "Define.h" が追加されたではありませんか。そして、全てのエラーは解消されました。そう、#include で指定したファイルの内容は、読み込み先でそのまま使用可能になるのです。本来は自分で #include を書くのですが、VS2022 ではエラー解決提案能力が高く、かなりの部分で自動化できます。折角なので、この恩恵にってみては如何でしょうか。
※ エラー解決は完全ではないのでエラい目に遭う事もあります。自分で判断してください…

この include は何でもかんでも無条件に読み込めます…が、当然 C言語および VS2022 の文法に則った形式のファイルじゃないとエラーとなります。

この Define.h は別途保存しておいて、別のプロジェクトでも追加/既存の項目で読み込めます。ラックチンですよね。


  • まとめ
  1. エスケープシーケンスの種類
  2. 文字に色を付ける
  3. 別名定義
  4. ファイル分割

>> C言語009 if 条件判定に進む
>> C言語基礎講座インデックスに戻る

※ 赤ちゃん用に思えるかもですが、コンセント口が上を向いているタップとかだと、これを填めておくと埃による失火を未然に防げます。