情報システム応用I 

情報システム応用I − 簡易データベースの構築

  1. データベース・ファイルを用意する.
  2. メインメニュー(メイン画面)を作成する.
  3. データベース・ファイルを読み込む.
  4. データを表示する.
  5. 終了時の処理を行う.
  6. 新規データを追加する
  7. 既存データを削除する
  8. データベース・ファイルを更新する(書き込み).

1. データベース・ファイル

sample.txt
nagai	19	163.3	58.4
aoyama	22	173.4	68.9
suwa	28	174.5	75.4
iwanaga	32	176.3	73.1
inagaki	33	168.9	59.6
tanaka	34	179.6	75.3
sugi	45	172.1	78.3
一人あたり一行.タブ区切りで,名前・年齢・身長・体重を書く.
データ型との対応:
 名前	char*型
 年齢	int型
 身長	double型
 体重	double型

 

年齢の若い順に並べておくこととする.

 

2. メインメニュー

tdss.c
#include "para.h"

int SelectNumber( void)
{
  int num;
  char buf[100];

  do {
    printf("\n\n---  Tiny Database ---");
    printf("\n 1) show all members");
    printf("\n 2) create a new member");
    printf("\n 3) delete a member");
    printf("\n 4) write to file");
    printf("\n 5) exit");    printf("\n>");
    fgets( buf, sizeof( buf), stdin);
    sscanf( buf, "%d", &num);
  } while ( num < 1 || num > 5 );
  
  return num;
}

int main( int argc, char **argv )
{
  int status;
  Person *psn;

  do {
    status = 0;
    switch( SelectNumber() ){
      case 1: printf("\n select 1"); break;
      case 2: printf("\n select 2"); break;
      case 3: printf("\n select 3"); break;
      case 4: printf("\n select 4"); break;
      case 5: printf("\n select 5\n\n"); 
              status = 1; break;
    }
  } while( status != 1 );
  
  return 0;
}
同じフォルダに用意してますか?ここ

項目選択画面の関数.




do〜whileの制御構文
 1: 全データの表示
 2: データの新規追加
 3: データの削除
 4: 上書き保存
 5: 終了

fgetsとsscanfのコンビにて「キー入力」に対応する.
numに整数値を渡す.
 ただし,1〜5の値でなければダメ.
 
numの値を返す.


main関数.argcとargvって何?

プログラム実行中・終了を表すフラグ.
Person型ポインタ変数を宣言する.

do〜while で制御する.
何も選択していない状態を表す.
SelectNumber関数を呼び出す.1〜5のいずれかが返ってくる.
 "select 1"と表示.
 "select 2"と表示.
 "select 3"と表示.
 "select 4"と表示.
 "select 5"と表示.
 status=1はプログラムの終了状態を表すことにする.

status=1でない限りは続行.

 

SelectNumber関数
fgets( buf, sizeof( buf), stdin )
sscanf( buf, "%d", &num )

3. データベースファイルの読み込み

main関数
    if ( argc < 2 ) {
      fprintf( stderr, "\n usage: tdss [file]\n\n" );
      exit(1);
    }
    psn = ReadFile( argv[1] );
オプション付の実行をしているかどうか?
  なければ使い方を表示して,
  強制終了.
  
  ReadFile関数を呼び出す.

 

SetName関数
SetName関数
static char *SetName( char *name)
{
  char *setname;

  setname = malloc( sizeof(char)*( strlen(name) +1 ));
  strcpy( setname, name);

  return setname;
}
char*型の関数



nameの文字数分だけメモリを確保する.
nameの内容をsetnameの内容にコピー.

先頭アドレスを返す.

 

strlen( name);
strcpy( setname, name);
GenerateNew関数
GenerateNew関数
static Person *GenerateNew( 
                    char *name, int age, double h, double w)
{
  Person *new;

  new = malloc( sizeof( Person) );
  new->name = SetName( name);
  new->age  = age;
  new->height = h;
  new->weight = w;

  return new;
}
Person型の関数.
  引数が多いので注意すること.


一人分のメモリ領域を確保.その先頭アドレスはnew.
SetName関数を呼び出して,名前を格納する.
年齢を格納する.
身長を格納する.
体重を格納する.

一人分のデータがメモリ上に配置された.
その先頭アドレスを返す.

 

ReadFile関数
ReadFile関数
static Person *ReadFile( char *file)
{
  char d_name[20];
  double d_height, d_weight;
  int d_age;
  FILE *fp;
  Person *top, *work;
  
  fp = fopen( file, "r");
  if ( fp == NULL ) {
    fprintf( stderr, "\n\n[%s] cannot open!\n\n", file );
    exit(3);
  }
  
  printf("\nReading [%s] ...", file );

  if ( !feof(fp) ) {
    fscanf( fp, "%s\t%d\t%lf\t%lf\n", 
	    d_name, &d_age, &d_height, &d_weight );
    top = work = GenerateNew( d_name, d_age, d_height, d_weight);
  }

  while ( !feof(fp) ) {
    fscanf( fp, "%s\t%d\t%lf\t%lf\n", 
	    d_name, &d_age, &d_height, &d_weight );
    work->next = GenerateNew( d_name, d_age, d_height, d_weight);
    work = work->next;
  }
  work->next = NULL; work = NULL;

  fclose( fp);

  return top;
}
main関数から呼び出されるPerson型の関数

名前を保持するために使う.
身長・体重を保持するために使う.
年齢を保持するために使う.
ファイル・ポインタ
データリスト用のポインタ.

fileという名のファイルのオープン
オープンに失敗したら...
  オープンでけまへん,とのメッセージを出力,
  強制終了する.


読み込み中,のメッセージを出力

もしファイルの最後(End Of File)でなければ...
  ファイル一行(一人分)のデータを獲得
  (データベースファイルの構成に留意),
  メモリにセットする.
以上は一人目用.

リストを作っていく.
この部分は下図を参考に.
現在の「次の人」にデータを格納していく.
ポインタを進める.


リストの最後を表すNULL.

ファイルを閉じる.

先頭アドレスを返す.

 

リスト構造(片方向リスト)を理解する
図(1). リストの先頭アドレスを覚えておく.
図(2).GenerateNew関数により,一人分のデータを格納する.
図(3).次の人のデータ格納先を指定する.
図(4).リストの最後はNULLを指定しておく.
FILE *fp; − ファイル構造体
fp = fopen( file, "r" ); − ファイルをオープンする.
fclose(fp); − ファイルを閉じる

4. データの一覧表示

ShowPerson関数
void ShowPerson( Person *p)
{
  Person *work;

  printf("\nAll members:");
  for ( work = p ; work != NULL ; work = work->next ) {
    printf("\n%s\t%d\t%.1lf\t%.1lf", 
	    work->name, work->age, work->height, work->weight );
  }
}
void型の関数.(値を何か返す必要はないので.)
  リストの先頭アドレスを引数にとる.



forループ: リストの先頭から最後(NULL)まで.

 

ShowPerson関数

 

※ ここまでのソースリストはこちら にあります.

5. プログラムの最終処理

FinalizePerson関数
void FinalizePerson( Person *p)
{
  Person *work;

  while( p != NULL ) {
    work = p;
    p = p->next;
    free( work->name);
    free( work);
  }
}
void型の関数



リストの最後に辿り着くまで以下の処理を繰り返す.
  現在の場所(アドレス)は覚えておく.
  pをさらに次のアドレスに進めておく.
  現在の名前データを解放した後に...
  現在のデータ領域すべてを解放する.
  
ちなみに,最後の段階ではリストの先頭(アドレス)
はどこかわからない.

 

FinalizePerson関数
free( psn);

6. 新規データの追加

NewPerson関数
Person *NewPerson( Person *p)
{
  Person *new, *here, *prev;
  char d_name[20], buf[100];
  double d_height, d_weight;
  int d_age;

  printf("\nName? "); 
  fgets( buf, sizeof( buf), stdin);    
  sscanf( buf, "%s", d_name);

  do {
    printf("His/Her age? "); 
    fgets( buf, sizeof( buf), stdin);    
    sscanf( buf, "%d", &d_age);
  }while( d_age <= 0 );

  printf("His/Her height? "); 
  fgets( buf, sizeof( buf), stdin);    
  sscanf( buf, "%lf", &d_height);

  printf("His/Her weight? "); 
  fgets( buf, sizeof( buf), stdin);    
  sscanf( buf, "%lf", &d_weight);

  for ( here = p ; here != NULL ; here = here->next ) {
    if ( here->age >= d_age ) break;
    else prev = here;
  }

  new = GenerateNew( d_name, d_age, d_height, d_weight);

  if ( here == p ) {
	new->next = p;
	return new;
  }
  else {
    new->next  = prev->next;
    prev->next = new;
    return p;
  }
}
Person*型の関数.
  リストの先頭アドレスを引数にとる.
・格納場所を知るためのポインタを用意する.
・名前・年齢・身長・体重を一時的に保持するための
  変数を用意する.
  
  
名前を入力する.
  fgetsとsscanfのコンビネーションを利用.



年齢を入力する.

名前が0歳未満にならないように制御している.
(上限を設けるには?各自考えてみよう.)

身長を入力する.



体重を入力する.



リストの先頭(p)から最後尾まで調べる.
新規入力の"年齢"より大きい値が見つかればforループから
抜け出す.そうでなければ,現在の場所の「一つ前」の
ポインタを保持しつつ,リストを辿っていく.

GenerateNew関数を用いて,新規データをメモリに格納する.
リストに格納する.
リストの先頭に挿入する場合.新規データの次は"現リストの先頭"新規データの先頭アドレスを返す

リストの途中に挿入する場合.
下図を参照のこと.

 

NewPerson関数

 

リストへの挿入

 

※ ここまでのソースリストはこちら にあります.

7. データの削除

DeletePerson関数
Person *DeletePerson( Person *p)
{
  Person *here, *prev;
  char d_name[20], buf[100];
  int d_age, find = 0;

  printf("\nName? "); 
  fgets( buf, sizeof( buf), stdin);
  sscanf( buf, "%s", d_name);

  printf("His/Her age? "); 
  fgets( buf, sizeof( buf), stdin);
  sscanf( buf, "%d", &d_age);

  for ( here = p ; here != NULL ; here = here->next ) {
    if ( strcmp( d_name, here->name) == 0 && 
         d_age == here->age ) {
      find = 1; break;
    }
    else prev = here;
  }

  if ( find != 1 ) 
    printf("\nNo such person!\n");
  else {
    if ( here == p ) 
      p = p->next;
    else   
      prev->next  = here->next;

    free( here->name); 
    free( here);
  }
  
  return p;
}
Person*型の関数
 リストの先頭アドレスを引数にとる.
・対象データの場所を知るためのポインタを用意.
・名前を年齢を一時的に保持するための変数を用意.
findは対象が見つかったかどうかのフラグを表す.

名前を入力.
d_nameに保持する.


年齢を入力.
d_nameに保持する.


リストの先頭(p)から最後尾まで調べる.

名前と年齢が一致するデータを探す.
見つかればフラグを立て,ループから抜ける.

"直前"のポインタを保持しておく.


対象データが見つからなかった場合の処理.

対象データが見つかった場合:
 リストの先頭にある場合先頭を"二番目のデータ"にする
 リストの途中にある場合削除対象を飛ばす.下図参照のこと.
 
削除対象のデータをメモリから解放する.



現在のリストの先頭アドレスを返す.

 

DeletePerson関数

 

リストからの削除

 

8. データベースファイルの更新(書き込み)

WritePerson関数
void WritePerson( char *file, Person *p)
{
  FILE *fp;
  Person *work;
  
  fp = fopen( file, "w+");
  if ( fp == NULL ) {
    fprintf( stderr, "\n\n[%s] cannot open!\n\n", file );
    exit(3);
  }
  
  printf("\nWriting [%s] ...", file );

  for ( work = p ; work != NULL ; work = work->next )
    fprintf( fp, "%s\t%d\t%lf\t%lf\n", 
	     work->name, work->age, 
		 work->height, work->weight );
  fclose( fp);
}
void型の関数(引数はファイル名とリストのポインタ)
 ・ファイル名はargv[1],これをfileで受ける.
ファイル・ポインタ
一時利用のポインタ(リスト用)

ファイルをオープンする.
オープン失敗時の処理






リストの先頭から最後尾まで回す.
ファイルの1行目からデータを書き込んでいく.
・フォーマット部分ReadFile関数のfscanfと
 まったく同じであることに注意.
ファイルを閉じる.

 

WritePerson関数

9. 終了・まとめ

機能を拡張してみよう
データの並べ替え
検索
統計処理

 

 TOP