第47回
特殊な画面制御~コンソール入出力関数とエスケープシーケンス

コンソール入出力関数

多くのCの処理系には、標準入出力関数とほぼ同等の機能を持ちながら、コンソールと直接やり取りをしてデータを入出力する関数群が用意されています。

標準入出力関数のメリット

これまで、データの入力にはscanfやgetchar、データの出力にはprintfやputchar、putsなどの関数を用いてきました。これらは標準入出力(standard Input / Output)を使った機能で、OSの種類に関わらず使用できます。

Linux向けに作ったプログラムのソースをMS-DOSに移植するような場合に、少なくとも標準入出力関数の部分はソースを書き直す必要がなくなります。これは書き直しの手間を省くだけではなく、書き直しによって発生するかもしれないバグの混入を防ぐ、という意味でも大きなメリットです。

汎用であるがゆえのデメリット

しかし、汎用的に使用できる標準入出力関数は、汎用であるがゆえに不便な部分もあります。たとえば、ユーザーに確認を求める処理でgetchar関数を使った場合、
[Y]{Enter]
のように、たった1文字入力するだけでも[Enter]キーを押さなければならず、ユーザーに余計な手間をかけることになります。Y(Yes)またはN(No)のような単純な入力では、[Y]または[N]キーを押すだけで続く[Enter]キーは不要――という仕様のほうが親切です。

[Enter]キーを押さなくても押したキーの内容を受け取ることのできる処理は、OSの提供する標準入力を避けて、コンソール(キーボードやディスプレイそのもの)と直接データをやり取りすることで実現できます。

コンソール入出力関数

そのための関数を「コンソール入出力関数」などと呼び、多くの処理系にはそれらを集めた「コンソール入出力ライブラリ」が用意されています。LSI-Cでは、ソースの冒頭で“conio.h”を取り込むことによって、これらの関数を使用できます。

LSI-Cに用意されているコンソール入出力関数の一部を、表1に掲げておきます。これらはMS-DOSでしか利用できないOS依存の関数群です。

表1:LSI-C 86に用意されている主なコンソール入出力関数
cgets 機能 コンソールから文字列を入力
宣言 char *cgets(char *s);
説明 引数sのs[1]には入力された文字数、s[2]以降に入力された文字列を格納する。同時に戻り値として文字列へのポインタ(s[2])を返す。文字列の終端にはNULL(\0)が付加され、改行コードは格納されない。
cputs 機能 コンソールへの文字列出力
宣言 void cputs(const char *s);
説明 引数のポインタsで示す文字列をコンソールに出力する。
改行文字(¥n)は出力されない。
cscanf 機能 コンソールからの書式付き入力
宣言 int cscanf(const char *format, ...);
説明 入力元がコンソールに限定されていることを除いて、scanfと同じ機能を持つ。
引数formatの書式もscanfと同じ。
戻り値は値が正常に代入された変数の数。エラー時にはEOF。
cprintf 機能 コンソールへの書式付き出力
宣言 int cprintf(const char *format,...);
説明 出力先がコンソールに限定されていることを除いて、printfと同じ機能を持つ。
引数formatの書式もprintfと同じ。
kbhit 機能 コンソールからキー入力があるかどうかを調べる。
宣言 int kbhit(void);
説明 キーボードが押されていれば0以外の値、押されていなければ0を返す。
押されて入力されたキーの値はgetchまたはgetcheで受け取ることができる。
getch
getche
機能 コンソールからの1文字(1バイト)入力
宣言 char getch(void);
char getche(void);
説明 コンソールから1文字だけを入力する。[Enter]キーによる改行の入力は不要。
戻り値は入力された1文字。
getchは入力された文字をコンソールに表示(エコーバック)しない。
getcheは入力された文字をコンソールに表示(エコーバック)する。
putch 機能 コンソールへの1文字出力
宣言 char putch(char c);
説明 引数cをコンソールに出力する。
戻り値は出力された文字。

getch関数を使った例

コンソール入力を使った例を紹介しておきましょう。本コラムの第13回「エラーメッセージと対処方法(3)~開発手順の効率化」で、エディタでソースを編集~コンパイラでコンパイル――という処理をエラーが出なくなるまで繰り返すDOSのバッチファイルを紹介しました(リスト1)。

このバッチファイルの中で、プロンプトを表示して編集したソースを再コンパイルするか、終了するかをユーザーに問い合わせるためのプログラム“yesno.exe”を使いました。そのソースがリスト2のyesno.cです。

第13回ではコンソール入力を紹介していなかったので、ユーザーの入力を求める処理ではgetchar関数を使いました。従って、ユーザーは
[Y][Enter]
のように、必ず[Enter]キーを押さなければなりませんでした。この部分をリスト3のようにgetch関数に変更すれば、ユーザーは[Y]または[N]キーを押すだけで済みます。

★注意
サンプルでは、リスト2のyesno.cをex4701.c、リスト3のyesno.cをex4702.cとして収録しています。リスト1のバッチファイルで試す場合は、実行形式ファイルの名前を変更するか、バッチファイル内の“YESNO”の部分を書き換えてください。

リスト1:コンパイル・エラー修正・再コンパイルを自動実行するバッチファイル~CMK.BAT
ECHO OFF
CLS
IF [%1] == [] GOTO ERR
:START
    ECHO %1 コンパイル中...
    LCC -j -oC:¥CLANG¥EXE¥%1.EXE C:¥CLANG¥SRC¥%1.C > C:¥CLANG¥SRC¥%1.ERR
    IF ERRORLEVEL 1 GOTO EDIT
    GOTO SUCCESS
:EDIT
    C:¥WINDOWS¥SYSTEM32¥NOTEPAD.EXE C:¥CLANG¥SRC¥%1.ERR
                        ↑エディタは適宜書き換えてください

    YESNO 再コンパイルしますか?
    IF ERRORLEVEL 1 GOTO EXIT
    GOTO START
:SUCCESS
    ECHO コンパイルは成功しました!
    GOTO EXIT
:ERR
    ECHO CMK : ファイル名を指定してください。拡張子は不要です。
:EXIT

リスト2:プロンプトを表示して'y'または'n'の入力を受け付ける~yesno.c(ex4701.c)
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
  char strMessage[255] = "Are you ready?";
  int chAnswer;

  if (argc > 1)
    strcpy(strMessage, argv[1]);

  strcat(strMessage, " <y/n>");

  puts(strMessage);
  chAnswer = getchar(); -------- 1文字の入力を受け取る
  if (chAnswer == 'y' || chAnswer == 'Y')
    return(0);
  else
    return(1);
}

リスト3:getch関数を使ったyesno.c(ex4702.c)
/* --------------------------------------------------------------
   yesno.c -- メッセージを表示してY/Nの入力を待つ。
              [Y]なら0、[N]なら1を終了コードとして返す。
   -------------------------------------------------------------- */

#include <stdio.h>
#include <string.h>
#include <conio.h>

int main(int argc, char *argv[])
{
  char strMessage[255] = "Are you ready?";
  int chAnswer;

  if (argc > 1)
    strcpy(strMessage, argv[1]);

  strcat(strMessage, " <y/n>");

  puts(strMessage);
  chAnswer = getch(); -------- [Enter]なしの1文字入力
  if (chAnswer == 'y' || chAnswer == 'Y')
    return(0);
  else
    return(1);
}