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型 |
 
※ 年齢の若い順に並べておくこととする.
 
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でない限りは続行. |
 
main関数 | |
if ( argc < 2 ) { fprintf( stderr, "\n usage: tdss [file]\n\n" ); exit(1); } psn = ReadFile( argv[1] ); |
オプション付の実行をしているかどうか? なければ使い方を表示して, 強制終了. ReadFile関数を呼び出す. |
 
SetName関数 | |
static char *SetName( char *name) { char *setname; setname = malloc( sizeof(char)*( strlen(name) +1 )); strcpy( setname, name); return setname; } |
char*型の関数 nameの文字数分だけメモリを確保する. nameの内容をsetnameの内容にコピー. 先頭アドレスを返す. |
 
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関数 | |
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. ファイルを閉じる. 先頭アドレスを返す. |
 
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)まで. |
 
 
※ ここまでのソースリストはこちら
にあります.
FinalizePerson関数 | |
void FinalizePerson( Person *p) { Person *work; while( p != NULL ) { work = p; p = p->next; free( work->name); free( work); } } |
void型の関数 リストの最後に辿り着くまで以下の処理を繰り返す. 現在の場所(アドレス)は覚えておく. pをさらに次のアドレスに進めておく. 現在の名前データを解放した後に... 現在のデータ領域すべてを解放する. ちなみに,最後の段階ではリストの先頭(アドレス) はどこかわからない. |
 
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関数を用いて,新規データをメモリに格納する. リストに格納する. リストの先頭に挿入する場合. ・新規データの次は"現リストの先頭" ・新規データの先頭アドレスを返す リストの途中に挿入する場合. 下図を参照のこと. |
 
 
 
※ ここまでのソースリストはこちら
にあります.
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)から最後尾まで調べる. 名前と年齢が一致するデータを探す. 見つかればフラグを立て,ループから抜ける. "直前"のポインタを保持しておく. 対象データが見つからなかった場合の処理. 対象データが見つかった場合: リストの先頭にある場合 ・先頭を"二番目のデータ"にする リストの途中にある場合 ・削除対象を飛ばす.下図参照のこと. 削除対象のデータをメモリから解放する. 現在のリストの先頭アドレスを返す. |
 
 
 
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と まったく同じであることに注意. ファイルを閉じる. |