ACM/ICPC問題用入出力の書き方

Cの場合

まとめ:

入力

ほとんどの問題の入出力は scanf と printf で事足ります。 これらの関数を使うにはファイルの先頭に

#include <stdio.h>

と書いておきましょう。int 型の入力は次のようにします:

/* 入力データ例: 34 */
int n;
scanf("%d", &n);

scanfの型指定子 %d はint型(10進整数値)を表します(おそらく decimal の d)。 引数にはアドレスを渡さなければならないので忘れずに & をつけましょう。 この場合,変数nにint型の数字が読み込まれます。

少数(double型)や文字列(char型配列)を読み込みたい場合は,%lf (double = long float で lf)や %s (string の s)を使って,

/* 入力データ例: 4.86 5.23 */
double x, y;
scanf("%lf%lf", &x, &y);
/* 入力データ例: abcdef fedcba */
char str1[1025], str2[1025];
scanf("%s%s", str1, str2); /* => str1 = "abcdef",str2 = "fedcba" */

とします。配列を表す変数 str1 や str2 はもともと char* というポインタ(配列の先頭アドレスを指している)なので & は特に必要ありません。

scanfでは,自動的に「空白」を無視するようになっています。 ですから,上の文字列の例ではスペースを抜いて2つの文字列がstr1とstr2に代入されます。 ここでいうところの「空白」とは,連続する(1つ以上の)スペース(0x20),タブ(0x09),改行(LF=0x0AやCR=0x0D)です (カッコの中は16進数のASCIIコードです)。 改行コードはOSによって違っています(例えばWindowsはCRLF,MacはCR,UnixはLFです)が,scanfではあまり気にする必要はありません。 とにかく,スペースや改行は読み込まれないので,文字列処理の際には注意しましょう。

たまに「スペースも含んで一行全部を文字列に読み込みたい」ことがあります。 その際には gets が便利です。getsは

/* 入力データ例: This is a line with many spaces. */
char str[1025];
gets(str); /* => str = "This is a line with many spaces." */

とすれば使えます。 str には改行コードは入らず,その改行コードの部分が NULL 文字で置き換わっています。 ただし,getsを使用する際には注意点が二つあります。一つは,例えば入力データが

2
This is a line with many spaces.
This is another line with many spaces.

となっているような場合,これを

int n;
char str1[1025], str2[1025];

scanf("%d", &n);
gets(str1); /* => str1 = "" */
gets(str2); /* => str2 = "This is a line with many spaces." */

とすると str1 には何も読み込まれません。 というのも,scanfの時点では改行コードまで読み飛ばされておらず,gets(str1) は最初に改行コードに出会ってしまうからです。 これを回避するには,

scanf("%d ", &n);

というように%dの後にスペースを入れます。そうすると,空白類が全て読み飛ばされます。

もう一つの注意点は,例えばWindowsのメモ帳で入力データを作成してcygwin上のgccでプログラムをコンパイルして実行するような場合, 対応する改行コードが違っていてgetsでは問題となります(これはscanfでは読み飛ばされるので問題にはなりません)。 具体的には,WindowsではCRLFの2バイトが改行コードとなりますが,cygwinなどのUnix環境ではLFの1バイトが改行コードとなって, LFに出会うまで,すなわちCRまでもが文字列に代入されてしまいます。 入力ファイルの改行コードをLFとして保存するなどして対応してください。

文字列を読み込む問題で,データセット数(ループ回数)が指定されていなくて EOF(End of File,つまりファイルの終了)まで読み込め,という問題文がたまにあります。 その場合は,scanfであれば

while (scanf("%s", str) != EOF) {
  /* 何か処理 */
}

として,getsの場合は

while (gets(str) != NULL) {
  /* 何か処理 */
}

とします。ここらへんは忘れやすいので man を読めるようになっておきましょう。

出力

出力はprintfを使えばOKです。型指定子はscanfと同様で,例えば

printf("%d", n);   /* int n */
printf("%ld", n);  /* long n */
printf("%lf", n);  /* double n */
printf("%c", c);   /* char c */
printf("%s", str); /* char str[] もしくは char *str */

などがあります。小数点3桁目で四捨五入して出力するには,

printf("%.2lf", n); /* n = 4.246 なら出力は 4.25 */

とします。また,改行は \n を使って

printf("%d\n", answer)

と書けば数字の後に改行されます。 ちなみに,printfデバッグする際には,

printf("[(x,y)=(%d,%d)]\n", x, y);

というように [ ] で囲むようにするなどして,通常の出力と分けると見やすくなります。

ファイル入力

例年,国内予選ではプログラムは各自のコンピュータ上で動作させてその出力結果をサーバに送ればOKなので, ファイル入出力は不要で全て標準入力からで済ませた方が簡単です。 つまり,コンソール上で

$ ./a < A.txt

という感じで読み込ませてあげればOKです。 ただし,アジア地区予選の日本大会などではファイルから読み込めとの指示が出ることがあります。 その場合は,次のようにfopen, fclose, fscanf, fgetsなどを使います。

FILE *fp = fopen("A.txt", "r");

int n;
char str1[1025], str2[1025];

fscanf(fp, "%d", &n);
fscanf(fp, "%s ", str1);
fgets(str2, 1024, fp);

fclose(fp);

fopenで開くファイルを指定します。 2番目の引数は read モード,つまり読み込みモードで開くということで,ICPCでは書き込みは行わないので常にこれでOKです。 最後に fclose がありますが,これはなくても動くでしょう(OSが勝手にファイルを閉じてくれるはずです)。

fscanf は最初にFILE構造体のポインタ fp を指定してあげるだけです。 fgets はなぜか読み込む最大のバイト数が指定できるようになっていますので指定してあげましょう。 fgets は fp を最後に指定するのでちょっと覚え間違いをしやすいですね。

C++の場合

まとめ:

入力

入力は主に cin を使います。scanf を使ってもよいでしょう(その場合はCの場合を読んでください)。 cin を使う場合,ファイルの先頭に

#include <iostream>
using namespace std;

と書いておきましょう。入力は次のようにします:

int n;
double x, y;

cin >> n >> x >> y;

これで,

3
34.2 46.1

3 34.2 46.1

といった入力を読み込めます。 空白を無視するのは scanf と同様です。 scanfのように型指定子がありませんが,これは変数の型に合わせて自動的にやってくれるというわけです(少し便利ですね)。 なので文字列も同じように

/* 入力データ例: abcdef fedcba */
string str1, str2;
cin >> str1 >> str2; /* => str1 = "abcdef",str2 = "fedcba" */

とすればOKです。

「スペースも含んで一行全部を文字列に読み込みたい」場合は getline を使って,

/* 入力データ例: This is a line with many spaces. */
string str;
getline(cin, str); /* => str = "This is a line with many spaces." */

とします。strには改行コードは含まれません。

この場合も gets と同様の注意点があって,

2
This is a line with many spaces.
This is another line with many spaces.

となっているような場合,これを

int n;
string str1, str2;

cin >> n;
getline(cin, str1); /* => str1 = "" */
getline(cin, str2); /* => str2 = "This is a line with many spaces." */

とすると str1 には何も読み込まれません。 cin >> n の時点では改行コードまで読み飛ばされておらず,getline(cin, str1) は最初に改行コードに出会ってしまうからです。 これを回避するには,

cin >> n;
cin.ignore();
getline(cin, str1); /* => str1 = "This is a line with many spaces." */
getline(cin, str2); /* => str2 = "This is another line with many spaces." */

というように cin.ignore を使えばOKです。 ただし,入力データの改行コードがおかしな場合は動作しないので,入力データの改行コードには気をつけてください。

文字列を読み込む問題で,データセット数(ループ回数)が指定されていなくて EOF(End of File,つまりファイルの終了)まで読み込め,という問題文がたまにあります。 その場合は,cin >> str であれば

while (cin >> str) {
  /* 何か処理 */
}

として,getlineの場合は

while (getline(cin, str)) {
  /* 何か処理 */
}

とすればOKです。

出力

出力には cout を使います。型は自動的に判断されるので,型に関わらず

cout << n;

で出力できます。スペースを入れたい場合などはちょっと面倒ですが

cout << a << " " << b;

という感じで書きます。改行は endl で表されて,

cout << cnt << endl;

とすると改行されます。 cinに比べてそれほど使い勝手はよくないので,printfも併用することがままあります。

四捨五入して出力したい場合は,cout でも方法はありますが,素直にprintfを使った方が簡単です。

ファイル入力

cin をファイルに割り当てれば,後は通常通り使えます。 これには,まずファイルの先頭で

#include <fstream>

と宣言しておいて,後は main 関数の先頭で

ifstream cin("A.txt");

と書いておけばOKです。後は cin が今までと同様に問題なく使えます。

Javaの場合

TODO: 詳しくは後ほど...(というか分からないので教えてください。入力はScanner?) とりあえずJavaの解答例でも参考にしてください。

$Id: input_output.shtml 1282 2007-02-03 09:05:17Z SYSTEM $