meideru blog

家電メーカーで働いているmeideruのブログです。主に技術系・ガジェット系の話を書いています。

C言語で住所録を作ってみました

      2019/05/12

みなさん、ごきげんよう( ´∀`)

練習も兼ねて、C言語で住所録を書いてみました。GUIも何もないCUIベースの住所録です。

今日は、ソースコードを貼りたいと思います\(^o^)/

ソースコード

// CUI版の住所録

// 【保存形式】
// ・dat形式で保存
// ・保存する内容は「番号」「グループ名」「名前」「住所」
//  「電話番号」

// 【問題点】
// ・全ての動作において、ファイルをバッファに移すので、
//	 容量が多いとメモリが確保できなくてエラーを起こす可能性がある。
// ・入力が512バイトを超えると、バッファオーバーで
//  バグを起こす可能性がある。

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 保存パス
char *path = "address.dat";

bool Initialize();		// 初期化設定を行う
bool Add();				// 登録を行う
bool Show();			// 閲覧を行う
bool Search();			// 検索を行う
bool Delete();			// 削除を行う
bool AddData(			// Dataの登録を行う
	const char *number,
	const char *group,
	const char *name,
	const char *address,
	const char *tel);
long CalcFileSize();	// ファイルサイズを計算(ファイルオープンを行っていないこと)
int CountData();		// ファイルの登録件数を計算(ファイルオープンを行っていないこと)

struct DATA {
	char number[512];	// 番号
	char group[512];	// グループ名
	char name[512];		// 名前
	char address[512];	// 住所
	char tel[512];		// 電話番号
};

int main(void)
{
	
	bool loop = true;
	
	do {
		int menu;
		puts("--------------------------------------");
		puts("何をしますか? 1-登録、2-閲覧、3-検索、4-削除、5-終了");
		printf("入力:"); scanf("%d", &menu);
		puts("--------------------------------------");

		// メニュー分岐
		switch (menu)
		{
			case 1:	// 登録
				if (!Add()) puts("エラー");
				break;
			case 2:	// 閲覧
				if (!Show()) puts("エラー");
				break;
			case 3:	// 検索
				if (!Search()) puts("エラー");
				break;
			case 4:	// 削除
				if (!Delete()) puts("エラー");
				break;
			case 5:
				puts("終了します。");
				loop = false;
				break;
			default:
				puts("適切な値ではありません。");
				break;
		}
	} while (loop);

	return 0;
}

// 初期設定を行う
bool Initialize()
{
	FILE *fp;

	// ファイルが存在しない
	if ((fp = fopen(path, "rb")) == NULL) {
		puts("\"address.dat\"が存在しないので作成します。");
		if ((fp = fopen(path, "wb")) == NULL) {
			puts("作成できませんでした。");
			return false;
		} else {
			puts("作成完了。");
		}
	}
	fclose(fp);

	return true;
}

// 登録を行う
bool Add()
{
	char number_buf[512], group_buf[512],
		 name_buf[512], address_buf[512],
		 tel_buf[512];

	// 入力
	printf("登録を行います。\n情報を入力してください。\n");
	puts("--------------------------------------");
	printf("番号      :");	scanf("%s", number_buf);
	printf("グループ名:"); scanf("%s", group_buf);
	printf("名前   :"); scanf("%s", name_buf);
	printf("住所   :"); scanf("%s", address_buf);
	printf("電話番号 ;"); scanf("%s", tel_buf);

	// 登録
	if (!AddData(number_buf, group_buf, name_buf, address_buf, tel_buf)) {
		return false;
	}

	puts("--------------------------------------");
	puts("登録が完了しました。");

	return true;
}

// 閲覧を行う
bool Show()
{
	printf("閲覧を行います。\n一覧を表示します。\n");

	long size = CalcFileSize();
	int count = CountData();

	// 登録件数が0
	if (count == 0) {
		puts("--------------------------------------");
		puts("登録はありません。");
		puts("--------------------------------------");
		return true;
	}

	FILE *fp;
	if ((fp = fopen(path, "rb")) == NULL) {
		return false;
	}

	DATA *allData = (DATA *)malloc(size);

	// ファイルを読み出す
	fseek(fp, 0, SEEK_SET);
	fread(allData, sizeof(DATA), count, fp);

	fclose(fp);

	// 表示
	puts("--------------------------------------");
	for (int i = 0; i < count; i++) {
		printf("番号   :%s\n", (allData + i)->number);
		printf("グループ名:%s\n", (allData + i)->group);
		printf("名前   :%s\n", (allData + i)->name);
		printf("住所   :%s\n", (allData + i)->address);
		printf("電話番号 :%s\n", (allData + i)->tel);
		puts("--------------------------------------");
	}

	free(allData);

	return true;
}

// 検索を行う
bool Search()
{
	puts("検索を行います。");

	int select;
	char word_buf[512];

	// 検索項目を選択
	puts("何で検索しますか?1-番号, 2-グループ名, 3-名前, 4-住所, 5-電話番号");
	printf("入力:"); scanf("%d", &select);
	puts("--------------------------------------");

	// 検索項目に沿って結果を出力
	switch (select) {
		case 1:	// 番号で検索
			puts("番号を入力してください。");
			printf("番号:"); scanf("%s", word_buf);
			break;
		case 2:	// グループ名で検索
			puts("グループ名を入力してください。");
			printf("グループ名:"); scanf("%s", word_buf);
			break;
		case 3:	// 名前で検索
			puts("名前を入力してください。");
			printf("名前:"); scanf("%s", word_buf);
			break;
		case 4:	// 住所で検索
			puts("住所を入力してください。");
			printf("住所:"); scanf("%s", word_buf);
			break;
		case 5:	// 電話番号で検索
			puts("電話番号を入力してください。");
			printf("電話番号:"); scanf("%s", word_buf);
			break;
		default:	// エラー
			puts("無効な値です。メニューに戻ります。");
			return false;
	}

	long size = CalcFileSize();
	int count = CountData();

	FILE *fp;
	if ((fp = fopen(path, "rb")) == NULL) {
		return false;
	}

	// ファイル内容をバッファに移す
	DATA *allData;
	allData = (DATA *)malloc(size);
	if (allData == NULL) {
		fclose(fp);
		return false;
	}
	fread(allData, sizeof(DATA), count, fp);

	fclose(fp);

	// 検索する
	bool isFinded = false;	// 発見したかどうか
	switch (select) {
		case 1:	// 番号で検索
			// 検索を行う
			for (int i = 0; i < count; i++) {
				// 一致するものがあれば表示する
				if (strstr((allData + i)->number, word_buf) != NULL) {
					if (!isFinded) {
						puts("一致するものが見つかりました。一覧を表示します。");
						puts("--------------------------------------");
					}

					isFinded = true;

					// 表示する
					printf("番号   :%s\n", (allData + i)->number);
					printf("グループ名:%s\n", (allData + i)->group);
					printf("名前   :%s\n", (allData + i)->name);
					printf("住所   :%s\n", (allData + i)->address);
					printf("電話番号 :%s\n", (allData + i)->tel);
					puts("--------------------------------------");
				}
			}
			break;
		case 2:	// グループ名で検索
			// 検索を行う
			for (int i = 0; i < count; i++) {
				// 一致するものがあれば表示する
				if (strstr((allData + i)->group, word_buf) != NULL) {
					if (!isFinded) {
						puts("一致するものが見つかりました。一覧を表示します。");
						puts("--------------------------------------");
					}

					isFinded = true;

					// 表示する
					printf("番号   :%s\n", (allData + i)->number);
					printf("グループ名:%s\n", (allData + i)->group);
					printf("名前   :%s\n", (allData + i)->name);
					printf("住所   :%s\n", (allData + i)->address);
					printf("電話番号 :%s\n", (allData + i)->tel);
					puts("--------------------------------------");
				}
			}
			break;
		case 3:	// 名前で検索
			// 検索を行う
			for (int i = 0; i < count; i++) {
				// 一致するものがあれば表示する
				if (strstr((allData + i)->name, word_buf) != NULL) {
					if (!isFinded) {
						puts("一致するものが見つかりました。一覧を表示します。");
						puts("--------------------------------------");
					}

					isFinded = true;

					// 表示する
					printf("番号   :%s\n", (allData + i)->number);
					printf("グループ名:%s\n", (allData + i)->group);
					printf("名前   :%s\n", (allData + i)->name);
					printf("住所   :%s\n", (allData + i)->address);
					printf("電話番号 :%s\n", (allData + i)->tel);
					puts("--------------------------------------");
				}
			}
			break;
		case 4: // 住所で検索
			// 検索を行う
			for (int i = 0; i < count; i++) {
				// 一致するものがあれば表示する
				if (strstr((allData + i)->address, word_buf) != NULL) {
					if (!isFinded) {
						puts("一致するものが見つかりました。一覧を表示します。");
						puts("--------------------------------------");
					}

					isFinded = true;

					// 表示する
					printf("番号   :%s\n", (allData + i)->number);
					printf("グループ名:%s\n", (allData + i)->group);
					printf("名前   :%s\n", (allData + i)->name);
					printf("住所   :%s\n", (allData + i)->address);
					printf("電話番号 :%s\n", (allData + i)->tel);
					puts("--------------------------------------");
				}
			}
			break;
		case 5:	// 電話番号で検索
			// 検索を行う
			for (int i = 0; i < count; i++) {
				// 一致するものがあれば表示する
				if (strstr((allData + i)->tel, word_buf) != NULL) {
					if (!isFinded) {
						puts("一致するものが見つかりました。一覧を表示します。");
						puts("--------------------------------------");
					}

					isFinded = true;

					// 表示する
					printf("番号   :%s\n", (allData + i)->number);
					printf("グループ名:%s\n", (allData + i)->group);
					printf("名前   :%s\n", (allData + i)->name);
					printf("住所   :%s\n", (allData + i)->address);
					printf("電話番号 :%s\n", (allData + i)->tel);
					puts("--------------------------------------");
				}
			}
			break;
		default:
			// メモリ解放
			free(allData);
			return false;
	}

	// 発見できなかったとき
	if (!isFinded) {
		puts("--------------------------------------");
		puts("一致するものが見つかりませんでした。");
		puts("--------------------------------------");
	}

	// メモリ解放
	free(allData);

	return true;
}

// 削除を行う
bool Delete()
{
	puts("削除を行います。");
	puts("どの項目の削除を行いますか?【】内の番号を入力してください。");

	long size = CalcFileSize();
	int count = CountData();

	// 登録件数が0であれば返却
	if (count == 0) {
		puts("--------------------------------------");
		puts("登録されていないので削除できません。");
		puts("--------------------------------------");
		return true;
	}

	FILE *fp;
	if ((fp = fopen(path, "rb")) == NULL) {
		return false;
	}

	DATA *allData = (DATA *)malloc(size);

	// ファイルを読み出す
	fseek(fp, 0, SEEK_SET);
	fread(allData, sizeof(DATA), count, fp);

	fclose(fp);

	// 表示
	puts("--------------------------------------");
	for (int i = 0; i < count; i++) {
		printf("【%d】\n", i + 1);
		printf("番号   :%s\n", (allData + i)->number);
		printf("グループ名:%s\n", (allData + i)->group);
		printf("名前   :%s\n", (allData + i)->name);
		printf("住所   :%s\n", (allData + i)->address);
		printf("電話番号 :%s\n", (allData + i)->tel);
		puts("--------------------------------------");
	}

	// 削除する項目番号
	int deleteNumber;
	puts("どの項目を削除しますか?");
	printf("【番号】:"); scanf("%d", &deleteNumber);
	if (!((0 < deleteNumber) && (deleteNumber <= count + 1))) {
		puts("無効な入力です。メニューに戻ります。");
		free(allData);		// メモリ解放
		return false;
	}

	// ファイルを一旦削除する
	if (remove(path)) {
		free(allData);
		return false;
	}

	// ファイルオープン(書き込みモード)
	if ((fp = fopen(path, "wb")) == NULL) {
		free(allData);
		return false;
	}

	// 削除項目を飛ばして書き込む
	for (i = 0; i < count; i++) {
		// 削除項目の番号以外をファイルに書き込む
		if (!(deleteNumber == (i + 1))) {
			// カーソルを末尾に移動する
			fseek(fp, 0, SEEK_END);
			// 1つ書き込む
			fwrite(allData + i, sizeof(DATA), 1, fp);
		}
	}

	fclose(fp);
	free(allData);

	puts("削除が完了しました。");

	return true;
}

// Dataの登録を行う
bool AddData(const char *number, const char *group,
	const char *name, const char *address,
	const char *tel)
{
	// 構造体
	DATA newData;
	strcpy(newData.number, number);		// 番号
	strcpy(newData.group, group);		// グループ名
	strcpy(newData.name, name);			// 名前
	strcpy(newData.address, address);	// 住所
	strcpy(newData.tel, tel);			// 電話番号

	FILE *fp;

	// ファイルオープン(追加モード)
	if ((fp = fopen(path, "ab")) == NULL) {
		return false;
	}

	// 書き込み
	fwrite(&newData, sizeof(DATA), 1, fp);

	fclose(fp);

	return true;
}

// ファイルサイズを計算
long CalcFileSize()
{
	FILE *fp;
	if ((fp = fopen(path, "rb")) == NULL) {
		return false;
	}

	// ファイルサイズを特定
	long size;
	fseek(fp, 0, SEEK_END);
	size = ftell(fp);

	fclose(fp);

	return size;
}

// ファイルの登録件数を計算
int CountData()
{
	return CalcFileSize() / sizeof(DATA);
}

600行前後で書けました。

十分にデバッグして確認していないので、バグがある可能性があります(-_-;)

実行するときは注意してください(-_-;)

仕様

登録・閲覧・検索・削除の4つができます。

登録

住所録の登録は、番号(管理番号)・グループ名・名前・住所・電話番号を登録できます。

閲覧

登録してある住所録の一覧を表示できます。

検索

検索は、番号・グループ名・名前・住所・電話番号のどれかのキーで、できます。

一致するものを全て一覧表示します。

削除

一覧で表示するものから選んで、削除できます。

セーブとロードについて

セーブとロードは外部ファイルにて行います。

文頭のほうに書いてある「char *path = “address.dat”;」を書き換えれば、任意のディレクトリに任意のファイル名で保存できます。

問題点

登録情報に入力できる項目は512バイトまで

登録できる番号・グループ名・名前・住所・電話番号は512バイトまでです。(ヌル文字も含むので、正確には511バイトまでです。)

これ以上を超えると、バッファオーバーして誤動作します(-_-;)

極端に登録件数が多くなりすぎるとメモリが確保できなくて動作しなくなる恐れがあります

一覧の表示も削除も何もかもすべての動作は、一旦、ファイルのデータをPC上のバッファに移して実行しています。

なので、極端に登録件数が多くなり過ぎると、ファイルサイズが大きくなって、メモリが確保できなくなって、動作しなくなる恐れがあります。

まぁ、そこまで極端に多く登録する人は居ないと思うので、平気だと思いますが(^_^;)

 

ノシ

少し前に勉強のためにWindows API (Win32API)を利用して住所録(電話帳?)っぽいソフトウェアを作っているという話をしました。 とりあえず、一通り完成したのでソースコードをアップしたいと思います!GUIなどGUI(グラフィカルユーザインタフェース)は、以...

 - 技術系