構造体を使用したプログラミング2:交差点データのファイル入出力

目次

  1. 課題2:交差点データのファイル書きだし
  2. 課題3:交差点データのファイル読み込み
  3. 課題3−2:交差点データのファイル読み込み


1. 課題2:交差点データのファイル書きだし

 課題1のプログラムを修正し、キーボードから入力した交差点データを次の形式でファイル"map.dat"に
保存するプログラムを作って下さい。交差点情報は1つにつき1行に書かれます。また、隣接交差点は
交差道路数(points)分だけ並びます。


  交差点データ保存形式
  ‾‾‾‾‾‾‾‾‾‾
crossing_point(交差点数)
id, x, y, wait, name, points, next[0], … next[points-1]  ← cross[0]のデータ
id, x, y, wait, name, points, next[0], … next[points-1]  ← cross[1]のデータ
                                ・
                                ・
id, x, y, wait, name, points, next[0], … next[points-1]  ← cross[crossing_point-1]のデータ

 例えば、交差点数が3の場合次のようなファイルになります。
3
1, 12.000000, 23.000000, 11.000000, 長町, 1, 2
2, 34.000000, 56.000000, 90.000000, 西公園, 1, 1
3, 56.000000, 45.000000, 45.000000, 一番町, 2, 1, 2


 ファイルへの保存処理を行なう部分は、関数map_write()として作ります。
 解答例は時間が来たら表示します。

 注) 関数の作り方は前回までのテキストを参照して下さい。

/* writefile.c --- 交差点データのファイル出力 */
#include <stdio.h>
#include <string.h>

#define CrossingNumber  5  /* 交差点数=5 */
#define MaxName        50  /* 最大文字数50文字(半角) */

typedef struct {
	double x, y;           /* 位置 x, y */
} Position;                /* 位置を表す構造体 */

typedef struct {
	int id;                /* 交差点番号 */
	Position pos;          /* 位置を表す構造体 */
	double wait;           /* 平均待ち時間 */
	char name[MaxName];    /* 交差点名 */
	int points;            /* 交差道路数 */
	int next[5];           /* 隣接する交差点番号 */
} Crossing;


Crossing cross[CrossingNumber];


void map_write(char *filename, int crossing_number)
{
	FILE *fp;
	int i, j;

	fp = fopen(filename, "w");
	if (fp == NULL) {
		printf("File %s is not created\n", filename);
		return;
	}

	/* 交差点数の書き込み */
	fprintf(fp, "%d\n", crossing_number);

	for (i = 0; i < crossing_number; i++) {

	/* 関数fprintfを使って、構造体のデータをファイルへ書き出すプログラムを記入しなさい */
		fprintf(fp, "%d, %lf, %lf, %lf, %s, %d",
			cross[i].id, cross[i].pos.x, cross[i].pos.y,
			cross[i].wait, cross[i].name, cross[i].points);    

		for(j=0; j<cross[i].points; j++) {
			fprintf(fp, ", %d", cross[i].next[j]);
		}

		fprintf(fp, "\n");                       /* 改行 */

	}
	fclose(fp);
}


int main(void)
{
	int i,j;
	int crossing_number=0;                         /* 入力した交差点情報の数 */
	char YesNo[16];                                /* 入力継続の問い合わせに使用 */

	for (i = 0; i < CrossingNumber; i++) {         /* データの入力 */

		crossing_number++;                         /* 交差点数を1増やす */

		/* 構造体crossへデータを代入するプログラムを記入しなさい */

		printf("交差点番号=");
		scanf("%d", &(cross[i].id));               /* 交差点番号の入力 */
		printf("名前=");
		scanf("%s", cross[i].name);                /* 交差点名の入力 */
		printf("x座標=");
		scanf("%lf", &(cross[i].pos.x));           /* x座標の入力 */
		printf("y座標=");
		scanf("%lf", &(cross[i].pos.y));           /* y座標の入力 */
		printf("待ち時間=");
		scanf("%lf", &(cross[i].wait));            /* 待ち時間の入力 */
		printf("交差道路数=");
		scanf("%d", &(cross[i].points));           /* 交差道路数の入力 */
		for (j = 0; j < cross[i].points; j++) {    /* 交差道路数だけ繰り返し */
			printf("隣接交差点番号(No.%d)=", j);
			scanf("%d", &(cross[i].next[j]));      /* 隣接交差点番号の入力 */
		}

		printf("次のデータを入力しますか?(Yes:\"y\")>");
		scanf("%s", YesNo);
		if (strcmp(YesNo, "y") != 0) break;        /* 入力文字が"y"ならforループを抜ける(break) */ 

	}

	for (i = 0; i < crossing_number; i++) {       /* データの表示 */
		printf("交差点番号:%d, 座標(%lf, %lf), 名前:%s,",
			cross[i].id, cross[i].pos.x, cross[i].pos.y,
			cross[i].name);
		printf(" 待ち時間:%lf, points:%d, adjacent crossing(", cross[i].wait, cross[i].points);

		for(j=0; j<cross[i].points; j++) {         /* 交差道路数だけ繰り返し */
			printf("%d ", cross[i].next[j]);
		}

		printf(")\n");
	} 

	/* ファイルへの書き込み */
	map_write("map.dat", crossing_number);
	
	return 0;
}



2. 課題3:交差点データのファイル読み込み

 課題1、2のプログラムを参考に、課題2で保存したファイル"map.dat"を構造体に読み込み、
交差点情報を画面に表示するプログラムを作って下さい。ファイル読み込み部分は既に下のプログラム中に
関数map_read()として書かれています。fscanf()の部分に注意して下さい。解答例は時間が来たら表示します。

 プログラムが完成したら、課題2で保存したファイル"map.dat"を実際に読み込み、交差点の各種情報が
正確に読み込まれているかどうか確認して下さい。このプログラムは今後のカーナビプログラムの基礎となり
ますので、確実に動くものを必ず作成して下さい。


/* readfile.c --- 交差点データのファイル入力 */
#include <stdio.h>

#define CrossingNumber 100 /* 交差点数=5 */
#define MaxName        50  /* 最大文字数50文字(半角) */

typedef struct {
	double x, y;           /* 位置 x, y */
} Position;                /* 位置を表す構造体 */

typedef struct {
	int id;                /* 交差点番号 */
	Position pos;          /* 位置を表す構造体 */
	double wait;           /* 平均待ち時間 */
	char name[MaxName];    /* 交差点名 */
	int points;            /* 交差道路数 */
	int next[5];           /* 隣接する交差点番号 */
} Crossing;


Crossing cross[CrossingNumber];


int map_read(char *filename)
{
	FILE *fp;
	int i, j;
	int crossing_number;                          /* 交差点数 */

	fp = fopen(filename, "r");
	if (fp == NULL) {
		printf("File %s is not created\n", filename);
		return -1;
	}

	/* はじめに交差点数を読み込む */
	fscanf(fp, "%d", &crossing_number);

	for (i = 0; i < crossing_number; i++) {


	/* 関数fscanfを使って、構造体のデータをファイルから読み込むプログラムを記入しなさい */

		fscanf(fp, "%d,%lf,%lf,%lf, %[^,],%d",
			&(cross[i].id), &(cross[i].pos.x), &(cross[i].pos.y),
			&(cross[i].wait), cross[i].name, &(cross[i].points));

		for(j=0; j<cross[i].points; j++) {
			fscanf(fp, ",%d", &(cross[i].next[j]));
		}

	}
	fclose(fp);

	/* ファイルから読み込んだ交差点数を返す */
	return crossing_number;
}


int main(void)
{
	int i,j;
	int crossing_number;                          /* 交差点数 */

	/* ファイルの読み込み */

	crossing_number = map_read("map.dat");

	for (i = 0; i < crossing_number; i++) {       /* データの表示 */
		printf("交差点番号:%d, 座標(%lf, %lf), 名前:%s,",
			cross[i].id, cross[i].pos.x, cross[i].pos.y,
			cross[i].name);
		printf(" 待ち時間:%lf, points:%d, adjacent crossing(", cross[i].wait, cross[i].points);

		for(j=0; j<cross[i].points; j++) {         /* 交差道路数だけ繰り返し */
			printf("%d ", cross[i].next[j]);
		}

		printf(")\n");
	}

	return 0;
}

さらに仙台市内の交差点データを
  map1.dat
  map2.dat
として用意しました。
これをコピーペーストでテキストファイル化し、保存してください。(または、wgetを使って、上記の地図データのURLを貼り付ければ簡単に取得できます。)
その後、まずmap1.datのデータが完成したプログラムで読み込めるかどうか試してみて下さい。
ただし、このファイルには90個の交差点データが入っていますので、プログラム中の CrossingNumber の定義を変更すること。加えて、ファイル名もちゃんと変えること。


3. 課題3−2:交差点データのファイル読み込み

map1.datが読み込めたひとは、map2.datが読み込めるようにしてみてください。
map2.datは、map.datにローマ字表記の交差点名を付け加えたものです。
構造体を一箇所修正し、対応するfscanfとprintfの箇所を直すだけです。
次回以降はmap2.datを使いますので、必ず動くようにしておいてください。


fscanfの警告について

コンパイル時に、
 "warning: ignoring return value of..."
のような警告が出る場合がある。
エラーでなく、警告なので無視しても特に問題はないが、気になるようだったら、
コンパイル時のオプションで "-Wno-unused-result" を付けると出力が抑制できます。

 
戻る