プログラミングのテクニック/入力の処理(C,C++)

Last-modified: 2012-04-05 (木) 23:41:37

C由来の物

競技プログラミングでは、速度の関係上、C由来の入出力関数を使い、C++由来の入出力関数を避ける事が推奨されている場合が多いです。(例: JOI)

scanf

scanfはCではstdio.h、C++ではcstdioヘッダで定義されている関数で、様々な種類の入力をうけて、それに沿った型の変数に受ける事ができます。

第一引数で「フォーマット文字列」と呼ばれる形式に沿って、入力の形式を指定し、第二引数以降の任意個の引数で、入力を受ける先の変数のポインタを指定します。

フォーマット文字列等の詳しい説明はWikipediaで。

主に注意すべきは第二引数以降がポインタである事です。

また、競技とは関係ありませんが、実用プログラミングだと%sで文字列を入力として受ける場合に%10sのように長さを指定しないと、バッファオーバーランと呼ばれるセキュリティホールの元になったりするので注意が必要です。(競技では与えられた条件に従えばこのような入力は与えられないという前提です)

fgets

スペースを含む1行を読みたい時に使用してください。その場合、全てをfgetsで受けて、必要に応じてsscanfでパースする方法をお勧めします(改行コードが面倒なので)。

C++由来の物

cin

入力の形式が空白ないし改行区切りである場合、

int n; double d; std::string s;
cin >> n >> d >> s;

のように >> で変数を繋げるだけで入力を読み取ることができます。型を意識せず >> で繋げるだけで入力を読み取ることができるためある意味では簡単とも言えます。

速度面については main 先頭で ios::sync_with_stdio(false); としておけばかなり改善されます。

文字列の読み込み

空白ないし改行区切りの場合、

std::string s;
cin >> s;

でも文字列は読み込めますが、場合によっては空白が入っていても1行分全てを読み込みたい場合もあります。この場合、

getline(cin, s);

とすることで1行分を読み込むことができます。なお改行文字自体は s には格納されず読み捨てされます。

なお、上述の >> と混ぜ合わせた場合、

// 数値<改行>文字列<改行> という入力形式とする
int n; std::string s;
cin >> n;
getline(cin, s);

例えばこのコードだと s には何も入りません。なぜなら >> の入力は1つめの改行文字の「前」で終わっているため、次の getline は改行文字だけを読み込んで終了するためです。これに対処するためには ignore を使うと良いでしょう。

// 数値<改行>文字列<改行> という入力形式とする
int n; std::string s;
cin >> n; cin.ignore(); // 次の改行文字まで(改行文字を含めて)最大1文字読み捨てる。
                             // 1文字だと駄目な場合は cin.ignore(/*読み飛ばしたい最大文字数*/); とすれば良い。
getline(cin, s);