文字列を分解する
1行の中に2つの情報が入っている
成績管理では、データを Tanaka,85 のような形で1行にまとめて持つことがよくあります。CSVと呼ばれる形式です。読み込んだ側から見ると、これは名前と点数がカンマで一緒くたになった1本の文字列にすぎません。使うには分解する必要があります。
行を読む
1行まるごと読むには fgets を使います。
char line[128];
while (fgets(line, 128, stdin) != NULL) {
...
}fgets は「最大 128 バイトまで」と器の広さを引数で受け取ります。前回やった考え方がそのまま標準関数にも入っています。読むものが無くなると NULL を返すので、それが繰り返しの終わりになります。
注意点が1つあります。fgets は 改行文字も一緒に読み込みます。"Tanaka,85\n" という中身になるので、そのままだと点数の後ろに改行がぶら下がります。先に消しておきます。
int i = 0;
while (line[i] != '\0' && line[i] != '\n' && line[i] != '\r') {
i++;
}
line[i] = '\0';改行の場所に '\0' を置くと、そこが文字列の終わりになります。バイトを削っているわけではなく、終わりの印を前へ動かしているだけです。'\r' も見ているのは、Windowsで作られたファイルの行末が2文字だからです。
区切りで切る
分解も同じ発想でできます。カンマを探して、そこへ '\0' を置きます。
int pos = 0;
while (line[pos] != ',' && line[pos] != '\0') {
pos++;
}
line[pos] = '\0';これで line は先頭から見ると "Tanaka" という文字列になり、&line[pos + 1] から見ると "85" という文字列になります。1本の配列の中に、終端を1つ増やしただけで2つの文字列が生まれました。コピーは1回も起きていません。
このやり方には代償があります。元の行は壊れます。カンマは '\0' に置き換わってしまったので、あとから「元の1行」を出したくなっても戻せません。標準の strtok も内部で同じことをしていて、渡した文字列を書き換えます。文字列リテラルを strtok に渡すと落ちるのはこれが理由です。
数字の側は atoi で整数に直します。stdlib.h が要ります。
int score = atoi(&line[pos + 1]);では、名前,点数 の行を何行でも受け取って、分解しながら合計を出すプログラムを書いてみましょう。
要件
- fgets で1行ずつ読み、行末の改行を取り除く
- カンマの位置にヌル文字を置いて、名前と点数に分ける
- 点数は atoi で整数に直す
- 各行を Tanaka 85点 の形で表示する
- 最後に 合計245点 の形で表示する
入出力例
main("Tanaka,85
Suzuki,70
Sato,90") → "Tanaka 85点
Suzuki 70点
Sato 90点
合計245点"
main("Ito,100
Kato,60") → "Ito 100点
Kato 60点
合計160点"
main("Nakamura,55") → "Nakamura 55点
合計55点"