C 言語でストリームから読み込む関数として、これまでに一文字単位(バイト単位)で行う fgetc
,
getchar
や、書式に従って読み込む scanf
を取り上げた。
これ以外に入力処理に良く使われる関数として fgets
関数がある。
fgets
関数の書式は次のようなものである。
char *fgets(char *, int, FILE *);
使用例
char buf[80]; FILE *fp; ... fgets(buf, sizeof(buf), fp);
上の例では、fgets
関数は、fp
で指定されるストリームから
最大sizeof(buf)
(80 である)よりも 1だけ少ないデータを読み込み、
buf
に格納する。ただし、EOF(ファイルの終端)もしくは、改行文字を
読み込んだ時点で終了する。読み込んだ文字の後ろには '\0' 文字が付け加えられ、
buf は文字列として正しいことが保証される。
従って、fgets
は、行単位の処理を行いたい場合等に良く用いられる関数である。
行全体が読み込まれたかどうかは、終端文字の1つ前が改行かどうかで判断することが可能である。
あるいは、strchr
関数を用いて、buf
中に '\n'
が含まれているかどうかで
判断しても良い。ただし、その場合には、最終行に改行文字が含まれていなかった場合の処理を考慮する必要がある。
上でのべた strchr
関数は次のような書式である。
char *strchr(const char *s, int c); char *strrchr(const char *s, int c);
使用例
#include <string.h> ... char *p; ... p = strchr(buf, '5'); if (p != NULL) { /* 文字 '5' が見つかった「場所」が p である。*/ *p = '6'; } else { /* 文字 '5' は見つからなかった */ ; }
上の例では、文字列 buf を先頭から走査し、最初に出現した文字 '5' を '6'に置き換えている。
文字列の終端から探索をしたい場合には strrchr
関数を用いる。
fgets関数の動きを確認するために、与えられたファイルの各行を引っくり返すようなプログラムを作成せよ。 たとえばファイルの中身が
This is a pen. That is a noteboook.となっていた場合、
.nep a si sihT .kooobeton a si tahT
と出力するようなプログラムを作成せよ。ただし、80文字よりも長い行はないと仮定して良い。
書式付きで読み込む scanf
の仲間に、与えられた文字列から書式にしたがって変数に値を設定する
sscanf
という関数がある。( s が一つ多い。) sscanf
関数の書式は次のようなものである。
int sscanf(const char * str, const char * format, ...); int scanf(const char * format, ...);
参考にあげた scanf
の書式と比較すると、
第1引数として文字列を表す変数が増えており、第2引数以下は、scanf
の引数と同じであることがわかる。
この sscanf
関数と上で出てきた fgets
関数を組み合わせることで、
scanf
で経験した入力がうまく行かない問題を解決できる。
scanf
関数で入力を行う際に、たとえば数値を期待している時にアルファベットをいれたりすると
思うような動作が起きなかった。
その理由は、scanf は変換ができなかった場合、読み込み位置が変化しないためである。
したがって、繰り返し処理の中で scanf
関数を使用していた場合は、最悪無限ループとなるような事態を経験しているはずである。
この問題に対応するために課題11では次のようなコードを提示した。
if (0 == scanf("%d", &human)) { while ('\n' != getchar()) { ; } human = -1; }
これは変換ができなかった場合は、改行文字まで入力ストリームの内容を読み捨てるというものであった。 そこでも述べたように、このような場合は通常次のようなコードが使われることが多い。
fgets(buf, sizeof(buf), stdin); if (0 == sscanf(buf, "%d", &human)) { human = -1; }
課題12 | 日程表 | 課題14 |