文字列を分解する

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]);

では、名前,点数 の行を何行でも受け取って、分解しながら合計を出すプログラムを書いてみましょう。

要件

  1. fgets で1行ずつ読み、行末の改行を取り除く
  2. カンマの位置にヌル文字を置いて、名前と点数に分ける
  3. 点数は atoi で整数に直す
  4. 各行を Tanaka 85点 の形で表示する
  5. 最後に 合計245点 の形で表示する

入出力例

main("Tanaka,85 Suzuki,70 Sato,90") → "Tanaka 85Suzuki 70Sato 90合計245点" main("Ito,100 Kato,60") → "Ito 100Kato 60合計160点" main("Nakamura,55") → "Nakamura 55合計55点"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.c
学習モード

メモ

文字列を分解する

⌘S で保存