演習中に配ったものよりも、while 文の終了条件がうまくできています。
/* prac0401.c */ #include <stdio.h> int split_number_name( char *line, int *dn, char *name, int len ) { int n, i, pos; n = 0; for( i = 0; i < 2; i += 1 ){ if( (line[i] < '0') || ('9' < line[i]) ) return 0; n = n * 10 + line[i] - '0'; } *dn = n; if( line[i] != '_' ) return 0; i += 1; pos = 0; while( line[i] != '.' ){ if( len <= pos ) return 0; switch( line[i] ){ case '\0': return 0; case '_': name[pos] = ' '; break; default: name[pos] = line[i]; } pos += 1; i += 1; } name[pos] = '\0'; return 1; } int main() { int dn; char line[100], name[100]; while( fgets(line,100,stdin) != NULL ){ if( split_number_name(line, &dn, name, 100) ) printf("%d\t%s\n", dn, name); } return 0; } |
nc_get の終了の仕方は、仕様が悪い。nc_get には返り値を持たせ、フォー マット通りの場合は 1 を返し、フォーマットと異なる場合は 0 を返し、入力 無しの場合は 2 を返すなどの工夫が必要である。
/* namecard.c */ // 第4回目のために拡張 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "namecard.h" // ================================================================== // アクセサ(構造体の変数を読み書きするための関数) // ------------------------------------------------------------------ // 変数 name の操作 // 指定位置の文字の読み書き void nc_put_name_at(NameCard *nc, char c, int pos) { nc->name[pos] = c; } char nc_get_name_at(NameCard *nc, int pos) { return nc->name[pos]; } // 名前の読み書き char *nc_get_name(NameCard *nc) { return nc->name; } void nc_put_name(NameCard *nc, char *str) { strncpy(nc->name, str, NAME_LEN); } // 名前の消去 void nc_clear_name(NameCard *nc) { nc_put_name_at(nc,'\0',0); } // ------------------------------------------------------------------ // 変数 age の操作 void nc_put_age(NameCard *nc, int n) { nc->age = n; } int nc_get_age(NameCard *nc) { return nc->age; } // ------------------------------------------------------------------ // ドライバーナンバーの操作 void nc_put_driver_number(NameCard *nc, int dn) { nc->number = dn; } int nc_get_driver_number(NameCard *nc) { return nc->number; } // ================================================================== // 構造体の確保と解放 NameCard *nc_open() { NameCard *ans; ans = (NameCard *)malloc(sizeof(NameCard)); if( ans == NULL ){ fprintf(stderr,"nc_open: malloc failed\n"); exit(1); } nc_put_age(ans, 0); nc_put_driver_number(ans, 0); nc_clear_name(ans); return ans; } void nc_close(NameCard *nc) { free(nc); } // ================================================================== // フォーマット通りの読み書き // 末尾の改行コードをカットする // ※ ここのファイル namecard.c の中だけで使う関数なので // static としている。 static void cut_return_code( char * line ) { int l; l = strlen( line ); if( line[l-1] == '\n') line[l-1] = '\0'; } // 1行のデータを分割 // ※ ここのファイル namecard.c の中だけで使う関数なので // static としている。 static int split_number_name_age(char *line, int *dn, char *name, int len, int *age) { int n, i, pos; i = 0; // i は、入力文字列 line の参照位置 // 数字を表す文字列を参照し、整数 int に変換 n = 0; while( line[i] != '\t' ) { // タブが正しい境界文字 if( (line[i] < '0') || ('9' < line[i]) ) return 0; n = n * 10 + line[i] - '0'; i += 1; } *dn = n; i += 1; // タブを読み飛ばす // 名前を表す文字列を参照 // 「(年齢)」が現れるまで、または「\0」までを参照 pos = 0; while( line[i] != '(' ){ if( len <= pos ) return 0; if( (line[i] == '\0') ){ // 「\0」のとき終了 name[pos] = '\0'; *age = 0; return 1; } name[pos] = line[i]; pos += 1; i += 1; } name[pos] = '\0'; i += 1; // 「年齢)」のうち年齢を抽出 n = 0; while( line[i] != ')' ) { if( (line[i] < '0') || ('9' < line[i]) ) return 0; // 「閉じ括弧で終了しなければスキャン失敗」 n = n * 10 + line[i] - '0'; i += 1; } *age = n; return 1; } // 1行データをファイルから入力する void nc_get(NameCard *dst, FILE *fin) { int dn, age; char line[NAME_LEN + 32]; char name[NAME_LEN]; if( fgets(line, NAME_LEN + 32, fin) == NULL ) exit(0); // 正常終了、仕様がよくない。。。 cut_return_code( line ); // 改行コードをカットする。 if( split_number_name_age(line, &dn, name, NAME_LEN, &age) ){ nc_put_driver_number(dst, dn); nc_put_name(dst, name); nc_put_age(dst, age); } else exit(1); // 異常終了、仕様が良くない。。。 } // 1行データをファイルに出力する void nc_put(NameCard *src, FILE *fout) { fprintf(fout,"%d\t%s(%d)\n", nc_get_driver_number(src), nc_get_name(src), nc_get_age(src)); } |