エディタを作る-1の3 エスケープシーケンス
早速、次のソースファイルを見てみよう。
解説が無いと 何をやっているのかすら分からない まるで暗号だが、これは昔から良く使われて来た【 エスケープ シーケンス 】= Escape sequence という コンソール画面の制御のためのコマンド群を使っているのだ。
1)エスケープ シーケンスが Win上で使えない
例えば、最初の //Clear とコメントされている行は、
① ESC+”[2j” と ② ESC+”[1;1H” の 2つのコマンドが実行されている
① は 画面をクリアする (ただし、カーソルの位置は先頭には移動しない)
② は カーソルの位置を先頭(左上 x=1,y=1)に移動する
つまり、「画面を消している」わけだ。
この ソースをもう少し判り易く、Visual Studioでもビルドが通るように改変すると、↓ こうなる。 MacのC言語では、文字列の中の ”¥e” で ESCコードとして扱ってくれるようだが、VSではこれが出来ない。
しかたなく、16進数 で ESC = 1Bh なので ”¥x1B” という 万国共通の書き方に直した。 (これなら どのC言語でも通用する)
これを ビルドして Windows10で実行すると ↓ こうなる。
文字列が そのまま表示されるだけで、意図する動作をしてくれない。
それも そのはず、Windows10 の コマンド プロンプト上で 「エスケープシーケンス」が動作しない事が 判明!!!
昔の MS-DOSの時代は、PC-9801版であれ AT版であれ、普通にエスケープシーケンスが動いていたよな? いや、Windows 98 とかでも 使っていた気がする・・・ どこから 使えなくなったんだ??
調べてみると、この機能が DOS系のOSで使えるのは、ANSI.SYSというデバイス ドライバが組み込まれているからであり、MS-DOSでは 大抵 標準で組み込む事になっていた・・・だから使える。 それが、Windows VISTAまでは、まだ ANSI.SYSが(System32内)に存在しているので、これを「組みこめば」使えるようになるらしい。
ところがである、Windows 7以降は、ANSI.SYS自体が供給されたいない(OS内に存在しない)のだ。 Microsoftは 何の嫌がらせか知らないが、 エスケープシーケンスを意図的に(使えないように)除外したらしい。
さて、どうしよう?
Qiitaの中でも書かれているが、コンソールの画面を消すくらいなら、
system("cls");
でも簡単に実行できるが、今度 それは Mac-OSで使えない。
とりあえず、まずは Windows10でも エスケープシーケンスを使えるようにする方法を検索して見よう。
2)便利なツール ANSICON 発見
探していると大変良く出来たツールを見つけた。
ANSICON (現在の最新は Ver 1.89)
これをネットからダウンロードして来て 解凍すると、OSの違いによって 32bit版と 64bit版に分かれている。 Windows 10なら ほとんどが64bitだと思うので、x64フォルダ側を使う。
どちらにも ansicon.exe が入っているので、これを -i のオプションスイッチを付けて実行してあげるだけで良い。
コマンドプロンプトを起動して、この ↑ フォルダまで移動して、
>ansicon -i
を実行しても良いのだが、cdコマンドで移動するのも面倒なので、GUI操作だけで済ませてしまおう。
① ansicon.exe の上で右クリックし、「ショートカットの作成」を実行
② その場に ショートカット・アイコンが出来たら、
その上でまた右クリックし → [プロパティ]
③ 「ansicon.exe のプロパティ」画面の「ショートカット」タブ内 の
「リンク先」に 実行するフォルダと 実行ファイル -i を追加する。
"D:・・・略・・・ ANSICON\ansi189\x64\ansicon.exe" -i
④ このショートカットアイコンを1回 実行すればOK
すると、コマンドプロンプトを起動し、今回のサンプルプログラムを実行すると ↓ こうなる。
ちゃんと画面クリアが実行され、その後 背景は白? 文字色は赤でhello,world が書かれている。(さらに 元の色=背景:黒、 文字=白に戻っている)
この ツールの便利な点は、1回 -i オプションで実行すると、Windowsシステムに常駐して、コマンドプロンプトを閉じて、開いてしても ずっと エスケープシーケンスが有効になったままになる点だ。 さらに言えば、Windowsを再起動しても有効である。
>ansicon -u
を実行しない限り 有効! これは 使える!!
と、思ったのもつかの間、あろうことか 統合環境で使えない事がわかった。
デバッグの「出力」窓では、エスケープシーケンスが有効にならず、文字がそのまま表示されてしまう ・・・
Visual Studioで作成した 実行ファイルでは有効に働くが、これでは デバッグができない。
やはり、system関数を使って 画面を消す(clsコマンド)とか、色を変える(colorコマンド)ようにしないといけないのか?
試しにプログラムでsystem(”cls”)を使って、画面を消すのは 問題無く出来たが、colorコマンドの挙動が エスケープシーケンスと異なることが判明した。
>color 背景色+文字色
背景色も文字色も 0~Fの16進数で指定。 例えば、背景=白、文字=黒 と反転させたい時は、
>color f0
で良い。 >color のみを (引数無しで)実行すると 元々の色に戻る。
色の一覧は、次の通り
ただ、これも問題がある。
背景色を指定すると、その後の文字列の背景だけ色が変わるのではなく、画面全体の背景の色が ↓ 変わってしまう。
color 7C の実行結果。 ⇒ 7=灰色 が 全体を塗ってしまう。
それでいて、 color 単独 (引数指定なし)で 元の 背景=黒に戻そうとすると、この時はなぜか、画面全体の背景色が変わるのでは無く その後に表示ちた文字列の背景だけが 黒に戻っている。
これでは 使えねーーー
/* ソリューション名 プロジェクト
* main.cpp (4_cls-color → cls-color)
*
* 画面消去&画面の文字に色を付ける。 システムコマンドで制御
*/
# include <stdio.h>
# include <stdlib.h>
# include <windows.h>
int main(int argc, char *argv[])
{
printf("hello,world.\n");
# if defined(_WIN32) || defined(_WIN64)
system("cls"); printf("CLS\n");
# else
printf("\x1B[2J\x1B[1;1H"); //clear(画面をクリアし、キャレット位置を左上にx,y=1,1)
# endif
# if defined(_MSC_VER)
system("color 7c"); printf("Color\n");
# else
printf("\x1B[31m\x1B[47m"); //color (白地に赤い文字)
# endif
# if defined(_MSC_VER)
system("color 07"); printf("colorデフォルトに戻す\n");
# else
printf("\x1B[39m\x1B[49m"); //reset(元の文字色に戻す)
# endif
for(int i=0; i<argc; i++) {
printf("receive > %s \n",argv[i]);
}
return 0;
}
こんな ↑ 感じのテスト・プログラムで動作テスト。
①ステップ実行での挙動と、②exeファイルを直接実行した時の挙動が違うのも気になる。
①だと思った通りに色も変わり、最後はデフォルトの背景色=黒、文字色=白に戻るのに、②だと背景色=灰色、文字色=赤のまま終わる(デフォルト色に戻らない)
いずれにしても、colorコマンドは 画面全体の背景と文字色を一気に変えるためのコマンドあって、文字単位に色を変える用途には向かないのは分かった
いっその事、WIN32/64のAPIで文字色を変えるコードを作った方が良さそうだ。
p.s.
Windows 10 の新しい版(TH2/1511以降)から、エスケープシーケンスに対応するようになったらしい。 「らしい」としたのは、まだ試していないとの TH2/1511の更新ファイルに不具合があって(更新失敗する?)、ダウンロードが停止されているからだ。
新しい Win10 では、SetConsoleMode呼び出しで エスケープシーケンスが使えるようになるとの事。 ただ、大半の 古いWin10(やWin7)の人がかなりの人数いる状態では、エスケープシーケンスだけで画面処理を作ったら、動かない人だらけになって、結局 これではダメってことになるように思える。
。