こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

QNo.4191217 ライブラリ関数について教えてくださいの質問をした者です。

質問を連投するのは気が引けたのですが、
どうしても分からなかったのでまた質問させてください・・・

ファイルの中のデータと配列データが一致するかを調べるプログラムを作ろうとしています。
前質問で、ライブラリ関数について教えていただき
以下のようにプログラムを作成してみました。
しかし、これだと結果が何も出力されませんでした・・・

何が間違っていて、どうすればいいのか詳しく教えていただけないでしょうか。
よろしくお願いします。

#include<stdio.h>

struct test {
char no[5];
char name[10];
char english[5];
char math[5];
};


int main(void){

FILE *fp;
int i;
char f_no[5], f_name[5];
struct test data[5] = {
{"001","akemi","100","40"},
{"002","tadao","59","76"},
{"003","mika","94","69"},
{"004","hiroshi","54","98"},
{"005","kazu","39","57"}
};

struct test *test_p;
test_p = data;


if ((fp = fopen("test.txt", "r")) == NULL) {
printf("NOT OPEN FILE\n");
}

else {

while (fscanf(fp, "%s%s", f_no, f_name) == 2) {

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

if((strcmp(test_p->no,f_no)==0) && (strcmp(test_p->name,f_name)==0) {
printf("%s %s 英語%s 数学%sです。\n", f_no, f_name, test_p->english, test_p->math);
}
else {
printf("%s %s 一致しません\n", f_no, f_name);
break;
}
++test_p;
}
}
}

fclose(fp);

}

test.txtの中身
001 akemi
002 kazuo
003 tadashi
005 mika
006 xxx

投稿日時 - 2008-07-20 22:11:59

QNo.4191696

すぐに回答ほしいです

質問者が選んだベストアンサー

> for(i = 0; i <= data[i]; i++) {
> と書けばいいんでしょうか?

配列の要素数を求めるには、sizeof() を使って、
sizeof(array) / sizeof(array[0])
と書くことがよくあります。
要するに、配列全体の大きさを先頭要素の大きさで割ると、
配列のサイズが求まるのです。

投稿日時 - 2008-07-20 23:28:52

このQ&Aは役に立ちましたか?

19人が「このQ&Aが役に立った」と投票しています

回答(8)

ANo.8

お手数ですが、最新のソースを見せてください。
入力ファイル test.txt の内容は変わってないですね?

投稿日時 - 2008-07-21 10:34:17

お礼

何度も親身にご回答いただきありがとうございました。
なんとか作成出来ました。
感謝致します。

投稿日時 - 2008-07-21 20:29:30

ANo.7

(プログラムのどこどこが悪い、ではなくて、「そもそも」について)

 ・プログラムは、データが変動するたびの再構築は不適、と考えます。
 ・ファイルは、データが変動するたびに再作成が適している、と考えます。

☆変動する試験結果は「ファイル」に、不変な名簿テーブルは「プログラム」に。

と思います。もっとも、質問者様のように「逆」であっても、

☆「プログラムの勉強」といわれれば、それまでですが・・。
--------------------------------------
「プログラムの勉強」ということで、ソースをいじくって(BorlandC++5.6.4)みました。

★それにつけても、学籍番号?と名前が一致するものが一人だけとは・・。

 プログラムデータ  ファイルデータ

   001 akemi    001 akemi
   002 tadao    002 kazuo
   003 mika      003 tadashi
   004 hiroshi    (なし)
   005 kazu      005 mika
     (なし)     006 xxx
-------------------------------------
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct{
 int iNum;
 char cName[16];
 int iEnglish;
 int iMath;
}TEST;

TEST sData[5] = {
 { 1, "akemi", 100, 40 },
 { 2, "tadao", 59, 76 },
 { 3, "mika", 94, 69 },
 { 4, "hiroshi", 54, 98 },
 { 5, "kazu", 39, 57 }
};
void main()
{
 FILE *fp;
 int i, iTblNum, iOk;
 char cTblName[16];

 if( ( fp = fopen( "test.txt", "r" ) ) == NULL ){

  printf( "NOT OPEN FILE\n" );

  exit( 1 );
 }
 while( 2 == fscanf( fp, "%d%s", &iTblNum, cTblName ) ){

  iOk = 0;

  for( i = 0; i < 5; i++ ){ // 人物特定

   if( iTblNum != sData[i].iNum ) continue; // 学籍番号
   if( strcmp( cTblName, sData[i].cName ) ) continue; // 名前

   printf( "%03d %-8s 英語 %3d 数学 %3dです。\n", iTblNum, cTblName, sData[i].iEnglish, sData[i].iMath );

   iOk = 1;

   break;
  }
  if( iOk ) continue;

  printf( "%03d %-8s さんの結果がありません。\n", iTblNum, cTblName );
 }
 fclose( fp );
}
注:インデントに全角空白を用いています。タブに一括変換して下さい。

投稿日時 - 2008-07-21 10:33:28

お礼

詳しいご説明ありがとうございます。
まだまだ勉強不足なのが身にしみてわかりました・・・

投稿日時 - 2008-07-21 20:27:36

ANo.6

配列の要素数をマクロ定義で求めるのであれば、引数に配列名を指定して

#define SIZE(array) (sizeof(array)/sizeof(array[0]))

なんて書くんでしょうね、きっと。
汎用的に使えますから。

投稿日時 - 2008-07-21 00:29:44

お礼

色々と教えていただきありがとうございます。
しかし、今のままでは動くことは動くのですが
表示結果が正しくありません・・・
他にどこが間違っているのでしょうか・・・

投稿日時 - 2008-07-21 10:20:01

ANo.5

勝手ながら、No.4 asuncionさんに補足しますと、配列の初期値がある場合は、マクロを使って以下のような書き方をすることが多いですね。
配列定義の要素数は省略すると、初期値のある分だけぴったり領域を確保してくれます。
# 配列の初期値がある場合は、こっちのほうが多いかな?

------------------------------------------------------------------------
struct test data[] = {
{"001","akemi","100","40"},
{"002","tadao","59","76"},
{"003","mika","94","69"},
{"004","hiroshi","54","98"},
{"005","kazu","39","57"}
};

/* データサイズを、配列サイズから算出 */
#define DATA_SIZE sizeof(data)/sizeof(data[0])

/* これ以降は、配列サイズには「5」の代わりに「DATA_SIZE」を使用する */
------------------------------------------------------------------------

あるいは、配列サイズとして、以下のように直接数値をマクロに設定してしまうことも結構あります。
# 特に配列に初期値がない場合は、このやり方が使われますね。
# 初期値がある場合でも使わないことはないと思うけど、ちょっと不自然な気がするかなぁ。。。
# ←あくまで個人的な感覚の話です。

------------------------------------------------------------------------
/* データサイズは「5」 */
#define DATA_SIZE 5

struct test data[DATA_SIZE] = {
{"001","akemi","100","40"},
{"002","tadao","59","76"},
{"003","mika","94","69"},
{"004","hiroshi","54","98"},
{"005","kazu","39","57"}
};

/* これ以降は、配列サイズには「5」の代わりに「DATA_SIZE」を使用する */
------------------------------------------------------------------------

要するに、「コードの中に数値を直接書くことは極力避けるような習慣をつけましょう」ということです。
あとで見たときにその数値が何を意味するものなのかがわからなくなるし、サイズを変えたいときに修正箇所が多くなって面倒ですよね。

ご参考まで。

投稿日時 - 2008-07-21 00:04:40

お礼

先に定義しておけば変更箇所が少なくなるのですね。
ご説明ありがとうございます。
しかし、動くことは動くのですが
表示結果は正しくありません・・・
もしよろしければ、他にどこが間違っているのかの
ご指摘をお願いします・・・

投稿日時 - 2008-07-21 10:17:09

ANo.3

> ロードするファイルの中のデータに
> 数値が入っているとは限らない

ロードするファイルとは、何のことですか? test.txt のことですか?
だとすると、test.txt には番号と名前が書いてあるだけで、
点数はプログラムの中に書いてありますよね。

ロードするファイルが test.txt 以外である、ということでしたら、
具体的な補足説明をお願いします。

投稿日時 - 2008-07-20 23:21:23

お礼

勘違いしていました、すみません。
test.txtは番号と名前だけなんで
点数は数値として書くべきですね・・・
ご指摘ありがとうございます。

投稿日時 - 2008-07-20 23:31:45

ANo.2

細かい話をすると、
英語と数学の点数がどうして「文字列」なのですか?
点数って、普通「数値」じゃないですか?

投稿日時 - 2008-07-20 22:48:32

お礼

普通は数値なのですが、
ロードするファイルの中のデータに
数値が入っているとは限らないものだとして作成したため、
すべて文字列として作成しました。

投稿日時 - 2008-07-20 23:04:05

ANo.1

そもそもコンパイルが通らないけど、
> char f_no[5], f_name[5];
f_nameの要素数が少ない。
> if((strcmp(test_p->no,f_no)==0) && (strcmp(test_p->name,f_name)==0) {
) が足りない。

この2点を修正すれば一応動く。


> struct test *test_p;
> test_p = data;
test_pの必要性は?
> ++test_p;
test_p = data;の代入をやり直しているところがないから、
もし5回インクリメントしてしまうとtest_pは参照できなくなる。
素直にdata[i]で参照すれば良いのに。

> for(i = 0; i<=5; i++) {
配列dataの要素数が5なのにループ回数が6回なのはおかしくないか?
できれば5はそのまま書かない方が良い。

> else {
> printf("%s %s 一致しません\n", f_no, f_name);
> break;
> }
一致しないときにループを抜けてしまうと、
その後に一致するデータがあっても見つけられない。
最後まで一致する組が無かったときに一致なしとするべきで、
ループを抜ける必要があるのはむしろ一致したとき。

あと、main関数の最後にreturn 0;が無い。

投稿日時 - 2008-07-20 22:40:32

お礼

ご指摘ありがとうございます。
分からないことがあったので、また質問させてください。

>> struct test *test_p;
>> test_p = data;
>test_pの必要性は?
>> ++test_p;
>test_p = data;の代入をやり直しているところがないから、
>もし5回インクリメントしてしまうとtest_pは参照できなくなる。
>素直にdata[i]で参照すれば良いのに。

struct test *test_p;
test_p = data;
の部分を削除して、data[i]を使えばいいってことでしょうか?

>> for(i = 0; i<=5; i++) {
>配列dataの要素数が5なのにループ回数が6回なのはおかしくないか?
>できれば5はそのまま書かない方が良い。

for(i = 0; i <= data[i]; i++) {
と書けばいいんでしょうか?

その他のご指摘は理解できました!
作成しなおしてみます。

投稿日時 - 2008-07-20 23:02:01