■[補足(2)] scanf()の問題

Last modified: Mon April 03 2017
このページは,基礎工学部システム科学科機械科学コース2年生向けの 「コンピュータ基礎演習」における演習1の教材の1つです.

きちんとしたプログラムでは, Cのライブラリ関数scanf()を使うべきではない. このページではそれを説明する.


何が問題か?

scanf()は,不適当な入力が与えられた場合, それを判断して処理を終了するが, 不適当な入力を除去することはしない. したがって,何度やり直しても,事態は改善されず, 例えば,下手なプログラムでは,無限ループに陥ったりする.

例プログラム: scanf.c

例1: 途中までは変換できる場合

"%d"で整数値を読み取ろうとしているところで, 「123a...」と入力したとする.

    +-----+-----+-----+-----+--...
    | '1' | '2' | '3' | 'a' | 
    +-----+-----+-----+-----+--...
       ↑
       ここから始まったとする.
                                   ↓ '1'を読み取って1つ前へ
    +-----+-----+-----+-----+--... 
    | '1' | '2' | '3' | 'a' | 
    +-----+-----+-----+-----+--...
             ↑
                                   ↓ '2'を読み取って1つ前へ
    +-----+-----+-----+-----+--... 
    | '1' | '2' | '3' | 'a' | 
    +-----+-----+-----+-----+--...
                   ↑
                                   ↓ '3'を読み取って1つ前へ
    +-----+-----+-----+-----+--... 
    | '1' | '2' | '3' | 'a' | 
    +-----+-----+-----+-----+--...
                         ↑
                                   ↓ 'a'を読み取って1つ前へ
    +-----+-----+-----+-----+--...    読み込み終了. 変換した数値: 123
    | '1' | '2' | '3' | 'a' |         戻り値: 1
    +-----+-----+-----+-----+--...
                               ↑
                                   ↓ 使わなかった'a'を読み戻す
    +-----+-----+-----+-----+--...
    | '1' | '2' | '3' | 'a' |      
    +-----+-----+-----+-----+--...
                         ↑
                        次に読み込むときはここから

例2: 最初から変換できない場合

"%d"で整数値を読み取ろうとしているところで, 「abcd...」と入力したとする.

    +-----+-----+-----+-----+--...
    | 'a' | 'b' | 'c' | 'd' | 
    +-----+-----+-----+-----+--...
       ↑
       ここから始まったとする.
                                   ↓ 'a'を読み取って1つ前へ
    +-----+-----+-----+-----+--...    読み込み終了.変換エラー.
    | 'a' | 'b' | 'c' | 'd' |         戻り値: 0
    +-----+-----+-----+-----+--...
             ↑
                                   ↓ 使わなかった'a'を読み戻す
    +-----+-----+-----+-----+--... 
    | 'a' | 'b' | 'c' | 'd' | 
    +-----+-----+-----+-----+--...
       ↑
       次に読み込むときはここから

再度scanf("%d", ...)が呼ばれても,同じことの繰返しになってしまう.


解決方法

scanf()は,1文字単位で入力を操作しているので, 不適当な入力を除去することには無理がある. そこで,fgets(), もしくは,getline.cで使われている getline 関数を用いて1行分をまとめて文字列に入力し, その後,sscanf()を使って,その文字列の中から数値を読み取るのがいい. もし,不適当な入力が検出された場合は,入力された行を全部棄てて, 新たな行の入力を求めるようにする.

例プログラム1: fgets+sscanf.c

例プログラム2: getline.c


誤作動なく整数、実数を格納するサンプルコード

参考として,整数,実数,文字列を誤作動なく格納するサンプルコードを載せる. データを格納するプログラムは,
整数はscanInt.c
実数はscanDouble.c
文字列scanStr.cである.
各自,適宜使用して,堅牢なプログラムを作成すること.

例プログラム3: scanInt.c

例プログラム4: scanDouble.c

例プログラム5: scanStr.c


[演習1のインデックス] [前の教材(補足(1):C言語におけるポインタの役割)]
hirai@me.es.osaka-u.ac.jp