【C言語】fgets関数とsscanf関数を使った入力処理をメモ

C言語/C++

今回はC言語の入力処理についてのメモ書きです。
入力処理を実装するとき、どの関数を使うか迷いませんか?
いくつか思い浮かびます。
・getc()
・gets()
・fgets()
・sscanf()

何をしたいかによって使う関数や組み合わせは変わってきます。
以下の内容を確認する時の環境は、ubuntuのgccで実装します。

以下で確認する内容

・標準入力から文字列として受け取る
・標準入力から整数(数値)として受け取る

ではさっそく確認してきます。

※以下は記載する方法はあくまで個人的な見解です。そのため参考程度

fgets関数で標準入力(キーボード)から文字列を取得

まずは、fgets()関数の仕様を先に確認します。

基本的な使い方

fgets関数の仕様
#include <stdio.h>
char* fgets(char *s, int size, FILE* stream);

【説明】
第3引数streamから第2引数(size – 1)個の文字列を第1引数sバッファに読み込む関数。
戻り値は成功するとsを返す。
終端の場合だとNULLを返す。
※注意ポイント:fgetsは読み込み時は改行は読み込まれる

以下はサンプルプログラムです。

#include <stdio.h>
/* マクロ */
#define BUF_SIZE    (16)
/* メイン処理 */
int main() {
    char buf[BUF_SIZE];
    fgets(buf, sizeof(buf), stdin);
    
    printf("inputString:[%s]", buf);

    return 0;
}

実行して出力結果を見てみるとわかりますが、
上記にも記載した通り、バッファ内に改行コードが含まれます。

以下、入力と出力例です。

/* 入力内容 */
abcdefghij
/* 出力結果 */
inputString:[abcdefghij
]

上記のように出力する時にに途中で改行されています。
そのため改行コードがある場合には改行コードを終端文字に上書きする処理を入れてあげる必要があります。

#include <stdio.h>
#include <string.h>
/*メイン処理*/
int main() {
    char buf[BUF_SIZE];
    char* p;
    fgets(buf, sizeof(buf), stdin);
    /* 以下追加 */
    p = strchr( buf, '\n' );
    if( p != NULL ){
        *p = '\0';
    }
    /* ここまで */
    printf("inputString:[%s]", buf);
    return 0;
}

バッファサイズ以上の文字数が入力された場合

先程のソースコードは、標準入力(キーボード)から受付を想定しています。
どのような文字が入力されるかはわかりません。
もちろんバッファサイズ以上の文字数が入力されるかもしれません。

入力文字数がバッファサイズより多い場合

今回はバッファサイズを16にして入力文字数を20文字にしてみましょう。
以下、fgets()関数を2回続けて行うプログラムです。

#include <string.h>
#include <stdio.h>
/* マクロ */
#define BUF_SIZE (16)
/* メイン処理*/
int main() {
    char buf[BUF_SIZE];
    fgets(buf, sizeof(buf), stdin);
    
    printf("[1]input string:[%s]\n", buf);
    
    fgets(buf, sizeof(buf), stdin);
    
    printf("[2]input string:[%s]", buf);

    return 0;
}

実行結果を確認すると、2回fgets()関数使ってのに2回目の入力を行わずに処理が終了します。

/* 実行結果 */
abcdefghijklmnopqr /* ←入力内容*/
[1]input string:[abcdefghijklmno] /* 出力*/   
[2]input string:[pqr              /* 出力*/
]

上記のような動作をした原因は、バッファサイズを超えた文字数が入力された場合には、それ以降の文字がストリームに残るためです。
図で説明します。

・入力された文字(20文字)

・1回目のfgets()読み込む

バッファサイズは16ですが、読み込む文字数はsize – 1なので15文字です。
※修正:最後[\n]->[\0]です。
・2回目のfgets()読み込む

・実行結果

※修正:1段目最後[\n]->[\0]です。
※修正:2段目最後[\n]->[\n][\0]です。

ということでバッファサイズが入力文字数を超えた場合には必要のないデータなどの場合は何らかの処理をしてやる必要があります。

非推奨な対策:fflash(stdin)を使う

標準入力のバッファをフラッシュする際にfflash(stdin)は未定義な動作となるため推奨されていません。
※ちなみに上記ソースコードでfflash(stdin)使ってもバッファをクリアはできませんでした。

推奨な対策:ストリームを空読み

推奨する方法は、次にfgets使う前にごみデータがストリームに残らないように空読みしてあげることです。
以下ソースコード。

fgets(buf, sizeof(buf), stdin);
    
    printf("[1]input string:[%s]", buf);
    /* ストリームを空読み.*/
    for (;;) {
        if (getchar() == '\n') { break; }
        if (feof(stdion)) { break; }
    }
    
    fgets(buf, sizeof(buf), stdin);

fgets関数で標準入力(キーボード)から文字列を数値として取得

次は、標準入力されたものを数値として受け取る方法です。
文字列として受け取るは、fgets()で確認しました。
数値として受け取る場合には、先程使ったfgets()sscanf()を組み合わせて使います。

sscanf関数の仕様
#include <stdio.h>
int sscanf(const char* str, const char* format, ...)

【説明】
第1引数strにセットした文字列を第2引数formatに従い変換。
第2引数に変換指定子を含むと変換したものを第3引数以降のポインタにセットする。
戻り値は、成功すると一致と代入が成功した入力要素の個数を返す。
注意:返却される値は渡された変換の個数よりも少ないことがある。最初に一致の失敗があった場合には0になることもある。

以下、ソースコード

文字列を整数(10進数)に変換する場合

#include <string.h>
#include <stdio.h>
/* マクロ */
#define BUF_SIZE (16)
/* メイン処理 */
int main() {
    char buf[BUF_SIZE];
    int n;

    /* 文字列読み込み */
    fgets(buf, sizeof(buf), stdin);
    /* 変換*/
    /* 変換指定子はdなので、符号付きの10進数の整数に対応*/
    int ret = sscanf(buf, "%d", &n);
    
	printf("ret[%d] %d\n",ret, n);
    return 0;
}

文字列を小数(10進数)に変換する場合

#include <string.h>
#include <stdio.h>
/* マクロ */
#define BUF_SIZE (16)
/* メイン処理 */
int main() {
    char buf[BUF_SIZE];
    double d;

    /* 文字列読み込み */
    fgets(buf, sizeof(buf), stdin);
    /* 変換*/
    /* 変換指定子はlfなので倍精度実数に対応 */
    int ret = sscanf(buf, "%lf", &d);

	printf("ret[%d] %f\n",ret, d);
    return 0;
}

コメント

タイトルとURLをコピーしました