問題
各行に1つ以上の整数値が記録されたファイルを処理するプログラムについて。
以下のようなinput.txt
が与えられる。
ファイルを読み込み、整数値を配列にいれる。
- 同じ行に記録された数値の間には、1つ以上の空白が挿入されている。
- 行数の範囲は1行から10000行まで
- 整数値の範囲は0から1,000,000 $(=10^6)$までとする。
- 各行の文字数は80文字未満(79文字以下)
例
10 20 30 40 50
12 3 4 5 6
100 333 440 303 202 202 22
1 11 11 11 1 1 11 1111
111111 222222 999999
12 22 33 444
99
:
10000 111111
環境
- windows10 Pro
- vscode June 2020 (version1.47)
- gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 (WSL)
注
ネット上に行ごとの要素数や文字間の空白がバラバラな場合のファイル入力に関する情報がほとんどなかったので書き残しました。
自分のメモ用かつ同レベルの初心者さん向けに書いたのでいろいろ変な書き方してるかもしれません。
この問題は大学の編入学試験の問題の一つです。ちょっと手こずったのでメモとして書いておきます。
もっと良い書き方、アルゴリズム、あったらお教えください m(>_<)m
ファイルの読み込み
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define N 10000
int main(){
FILE *fp;
//変数などはここに追加していく☆
if((fp = fopen("./input.txt", "r")) == NULL) exit(1);
//ここにファイルの処理 ☆
fclose(fp);
return 0;
}
以降☆がついたコメントの部分だけ書いていきます
行ごとに文字列を読み込む
fgets関数を使ってファイルのデータを行ごとに取得
char data[80];
unsigned int i;
for(i = 0; i < N && fgets(data, sizeof(data), fp) != NULL; i++){
//これで各行の文字列がdataに格納される
//この時点では文字列に空白を含む
printf("data [%d] %s\n", i, data);
}
/*出力
data [0] 10 20 30 40 50
data [1] 12 3 4 5 6
:
data [N] ???
*/
空白区切りの文字列を配列に格納(+数値に変換)
strtok関数を使って空白区切りで数値を取得
strtok関数は最初に文字列の先頭ポインタを渡したら、2回目以降はNULLを渡す。
atoi関数でcharからintに型変換
char data[80];
int num[N][80] = {{0}}; //ここにファイルの数値が格納される
char *te, *ee;
unsigned int i, j, k;
int temp = 0;
for(i = 0; i < N && fgets(data, sizeof(data), fp) != NULL; i++){
te = data; //文字列のポインタ
j = 0;
//eeに文字が入り、その文字の最後を'\0'で置き換え
while((ee = strtok(te, " \n")) != NULL) {
num[i][j] = atoi(ee); //数値に変換して配列に格納
te = NULL; //strtok()の2回目以降の呼び出しのためにNULLを格納
j++;
}
}
数値の処理
以下の様にnumを適当にいじれば数値を操作できる。
要素数を取得していないので、特定の位置のデータ、最大値、最小値、線形探索などしかできない。
上の段階でうまいこと要素数を得られれば、データ内の平均や標準偏差などを行ごとに取得できるかも。
0の地点で打ち切れば、要素数自体は得られるが、今回は整数値の範囲に0があるので断念。
// 配列のi行目の最初の整数値だけ表示
printf("%d\n", num[i][0]);
// 配列のi行目の最大値を表示
temp = num[i][0];
for(k = 1; k < (sizeof(num[i]) / sizeof(int)); k++){
if(temp < num[i][k]) {
temp = num[i][k];
}
}
printf("max = %d \n", temp);
コード全体
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define N 10000
int main() {
FILE *fp;
char data[80];
int num[N][80] = {{0}};
char *te, *ee;
unsigned int i, j, k;
int temp = 0;
if((fp = fopen("./input.txt", "r")) == NULL) exit(1);
for(i = 0; i < N && fgets(data, sizeof(data), fp) != NULL;i++){
te = data;
j = 0;
while((ee = strtok(te, " \n")) != NULL){
num[i][j] = atoi(ee);
j++;
te = NULL;
}
printf("first = %d\n", num[i][0]);
temp = num[i][0];
for(k = 0; k < (sizeof(num[i]) / sizeof(int)); k++){
printf("%d ", num[i][k]); //すべて表示
if(temp < num[i][k]){
temp = num[i][k];
}
}
printf("\n");
printf("max = %d \n", temp);
//printf("\n");
}
fclose(fp);
return 0;
}
欠点や留意点や他
- 配列が固定長なので、入れた数字以外はすべて0が入る。(今回の場合配列の個数が80個固定になる) ← 動的配列?にすればいい?
- 処理として、最大値、最小値を探す以外見つからない。 ← 要素数をうまいこと得られれば平均なども出せる
- input.txtの数値の並びに一定の規則がある場合、fscanf()で読み込もう
- 普通は要素数が決まっていたり、データの並びが決まっている場合がほとんどなのでこのような状況はほとんど無いかもしれない。
- そもそも要素数自体は任意の行の改行までをみて要素と空白をうまく分けて数えればいける?(方法がわからない)
追記・謝辞
下記のコメントで
- @fujitanozomu さんに各行の要素数の取得に関してアイデア
-
@angel_p_57 さんに
strtol()
を使った実装のアイデア
教えていただきました!勉強になります。ありがとうございます