• ベストアンサー

ファイルの読み込みと構造体

こんばんは☆ Cのプログラミングに困っています(>_<) テキストファイル(meibo.txt) 田中 32 公務員 佐藤 20 学生 というファイルを読み込んで、構造体に入れて表示するには どうしたらよいでしょうか? struct meibo{     char name[20];     int age;     char job[20]; }list[3]; としたあとにどのように入れたらよいかがわかりません… わかる方教えてくださいm(_ _)m

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

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.6

★全く分からんのですか? >としたあとにどのように入れたらよいかがわかりません…  ↑  これは文字列の表現の仕組みを理解していれば出来ると思います。  回答者 No.2 さんの補足にあるソースで strcpy() 関数を使わなでコピーすれば  上手くいくようになります。もちろんすべてが正しいわけではなく、考え方が  大よそあっているという事です。 HNDHDKさんのソース while(fgets(string,128,fp)){  for(n=0;string[n]!=" ";n++){   strcpy(list[i].name,string[n]);  }  for(n=n+1;string[n]!=" ";n++){  list[i].age=;  }  for(n=n+1;string[n]!='\0';n++){  strcpy(list[i].name,string[n]);  } } これを strcpy() 関数を使わないで処理すると while ( fgets(string,128,fp) != NULL ){  for ( n = 0 ; string[n] != ' ' ; n++ ){ ←スペースは文字定数で比較(文字列ではない)   list[ i ].name[ n ] = string[ n ];  }  list[ i ].name[ n ] = '\0'; ←末尾の'\0'(重要)    list[ i ].age = 0; ←ゼロを入れておく  for ( n++ ; string[n] != ' ' ; n++ ){ ←ここも文字定数で比較   list[ i ].age *= 10;   list[ i ].age += (string[n] - '0'); ←文字から数値に変換  }  for ( n++ ; string[n] != '\0' ; n++ ){   list[ i ].job[ n ] = string[ n ];  }  list[ i ].job[ n ] = '\0'; ←ここも末尾の'\0'(重要) } となります。 最後の要素は job メンバに入れるように修正しておきました。 こんな感じでいいです。 strcpy() 関数を使いたい場合は区切り文字(スペース)に '\0' をセットしてから 文字列の先頭を strcpy() 関数に渡すことでコピーできます。今回は文字単位で コピーする方法を紹介しました。改良してみて下さい。 >ageはint型なのでどのようにしたら? これも区切り文字(スペース)に '\0' をセットした後に atoi() 関数で文字列から 整数値に変換できます。今回は文字単位で整数値に変換してみました。 それが次の2行です。 >list[ i ].age *= 10; >list[ i ].age += (string[n] - '0'); ←文字から数値に変換 このようにすることで文字列から整数値に変換できます。 最後に: 文字定数と文字列定数を混同しないように注意して下さい。 また、文字の比較と文字列の比較も違いますので同じに考えないで下さい。 C言語では文字列の比較にstrcmp()関数で行います。

HNDHDK
質問者

補足

Oh-Orangeさん >list[ i ].age *= 10; >list[ i ].age += (string[n] - '0'); ←文字から数値に変換 の2行が理解しがたいのですが…これは、 決まりみたいなものなのでしょうか? 特に上の行の10を代入の意味は???

その他の回答 (8)

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.9

★回答者No.6です。 ・デバッグしてみました。  どうやら回答 No.6 のサンプルに一部間違いがありました。  それが原因で文字化けしていました。 修正版: for ( j = 0, n++ ; string[n] != '\n' ; n++, j++ ){  list[ i ].job[ j ] = string[ n ]; } list[ i ].job[ j ] = '\0'; 修正箇所1: ・job 配列の添え字に n を使っていたのが間違いです。  正しくは新しいカウンタ変数 j などを用意して添え字が 0 から始まるようにします。  これにより最後の '\0' も job[ j ] = '\0'; と j カウンタに変更します。 修正箇所2: ・for()文の条件式で string[n] != '\0' としていましたが、改行を job 配列に  含めないようにするため string[n] != '\n' と変更して下さい。 修正箇所3: ・meibo 構造体の job のサイズが 5 です。  質問にあるテキストファイル(meibo.txt)には『公務員』『学生』ですよね。  『学生』はともかく『公務員』はサイズが 6 です。'\0'文字も含めると 7 になります。  配列の容量が不足しています。job[ 10 ]; ぐらいに変更して下さい。 上記の3箇所を修正すれば正しく動きました。 なおスペースが 2 個以上連続していたり、名前が 9 文字以上だったり、または 職業名が 4 文字以上だと name、job の配列容量を越えます。この辺のチェックを 付け加えた方が良いでしょう。あとポインタを使ったり、strcpy()、atoi() 関数を 使って処理させる方法もあります。いろいろと工夫してみてください。

HNDHDK
質問者

お礼

>Oh-Orangeさん 本当に丁寧な解説ありがとうございました☆☆☆ おかげでこのやり方でもうまく表示できました(^o^) Oh-Orangeさんのお陰で、 文字列の処理(入門)の仕方が かなりわかりました(^o^)b 今度は自分の文字列処理の仕方を 見つけて、知識を深めたいと思います☆ ありがとうございました☆

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.8

★回答者No.6です。 ・補足より。文字列から整数値の変換の仕方を書きます。 >>list[ i ].age *= 10; >>list[ i ].age += (string[n] - '0'); ←文字から数値に変換 > >の2行が理解しがたいのですが…これは、 >決まりみたいなものなのでしょうか? >特に上の行の10を代入の意味は???  ↑  これを次のように書き換えてみます。  char str[] = "123";  int age = 0; ←初期化    for ( i = 0 ; str[i] != '\0' ; i++ ){   age = (age * 10) + (str[i] - '0');  }  とします。また、 >age = (age * 10) + (str[i] - '0');  の行は次の2行でも同じ処理になります。記述が違うだけ。  age *= 10;  age += (str[i] - '0');  こういうことです。 ・それではfor文を展開してみます。  i=0のとき age = (age * 10) + ('1' - '0'); … age =  0 + 1;⇒age=1  i=1のとき age = (age * 10) + ('2' - '0'); … age = 10 + 2;⇒age=12  i=2のとき age = (age * 10) + ('3' - '0'); … age = 120 + 3;⇒age=123  となって age = 123 がセットされます。  この仕組みが『文字列から整数値の変換』の決まりみたいな方法です。 ・以上。

HNDHDK
質問者

補足

Oh-Orangeさん ありがとうございます☆ とてもすばらしい解説でした(*^o^*) しかし、Oh-Orangeさんのプログラムを参考に作ってみたのですが、 どうしても job が文字化けしてしまいます(T_T) どこがおかしいでしょうか? #include <stdio.h> #include <stdlib.h> #define PROF 256 int main(void) {     struct meibo{         char name[20];         int age;         char job[5];     }list[10];     char FileName[20];     unsigned char string[PROF];     int i=0,j,n;     FILE *fp;     printf("入力ファイル名>>>");     scanf("%s",FileName);     if((fp=fopen(FileName,"r"))==NULL)     {         printf("ファイルが見つかりません。---%s\n",FileName);         exit(EXIT_FAILURE);     }     while(fgets(string,PROF,fp)!=NULL)     {         for(n=0;string[n]!=' ';n++)         {             list[i].name[n]=string[n];         }         list[i].name[n]='\0';         list[i].age=0;         for(n++;string[n]!=' ';n++)         {             list[i].age*=10;             list[i].age+=(string[n]-'0');         }         for(n++;string[n]!='\0';n++)         {             list[i].job[n]=string[n];         }         list[i].job[n]='\0';         i++;     }     for(j=0;j<i;j++)     {         printf("%s %d %s\n",list[j].name,list[j].age,list[j].job);     }     fclose(fp);     return EXIT_SUCCESS; }

回答No.7

初心者向きなら 以下のようなfscanfを使うやり方もあります   int i;   FILE *fp      fp = fopen("meibo.txt", "r");   i = 0;   while (!feof(fp))   {     fscanf(fp,"%s %d %s",list[i].name,&(list[i].age),list[i].job);     i++;   } 尚、プロは、scanfやfscanfの使用は嫌うようです。

HNDHDK
質問者

お礼

gyrocompasさん 初心者なので、このような解説もとてもありがたいです☆ シンプルなのですぐに組み込んでみたいと思います☆

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.5

> while(string!=" ") これは、ローカル変数のstringの「アドレス」と、プログラム中のどこかに確保された " "(メモリ上では 0x20 0x00)の「アドレス」を比較しています。 結果が等しくない時に真となりますので、ここの条件判定は常に真です。 # 値の書き換わる変数と、文字列定数の先頭アドレスが同一になることは絶対にありませんので。 表示されないのは、 strcpy(list[i].name,string); を無限ループで実行しているため、ここから先に進まないからではないでしょうか。 文字列の比較はstrcmp()を使用しましょう。 # C++の文字列クラスの場合、string!=" "と言う判定が可能になっている場合がありますが。

HNDHDK
質問者

お礼

Wr5さん なるほど☆ どおりで継続条件がうまくいかないわけですね… わかりやすい解説をありがとうございます☆

  • yama5140
  • ベストアンサー率54% (136/250)
回答No.4

★デリミタは、半角空白(0x20)ひとつとする。 ★最大レコード長は255バイトとする。 ★名前、職業には半角空白を含めない。 ★レコード数は30以内。 のファイルの条件があり、 ★構造体の職業項目には、復帰改行コードを含めない(◆)。 の作成仕様があるとき、 ☆基本部分のみでは、説明できないので・・ほぼ「丸受け」? #define un_char unsigned char typedef struct{  int iAge;  char cName[16];  char cJob[32]; }LIST; int toStruct( int iCntSpace, LIST sWork[], int nn, un_char cBuf[], int iTop ) {  int i, iCnt = 0;  for( i = 0; i < 256; i++ ){   if( 0x0D >= cBuf[i] ) cBuf[i] = 0x20; // 復帰改行コード(◆)   if( 0x20 == cBuf[i] ) iCnt++;    // 0x20 デリミタ   if( iCnt == iCntSpace ){    cBuf[i] = 0x00; // 要素の末端    if( 1 == iCnt ) strcpy( sWork[nn].cName, &cBuf[iTop] );    if( 3 == iCnt ) strcpy( sWork[nn].cJob, &cBuf[iTop] );    if( 2 == iCnt ) sWork[nn].iAge = atoi( &cBuf[iTop] );    return( i + 1 ); // 次の要素の先頭   }  }  return( 0 ); } void main() {  LIST sWork[30];  int i, nn = 0, iTop;  FILE *fp1;  un_char cBuf[256];  fp1 = fopen( "meibo.txt", "r" ); // File check(略)  while( NULL != fgets( cBuf, 255, fp1 ) ){   iTop = toStruct( 1, sWork, nn, cBuf, 0 );   iTop = toStruct( 2, sWork, nn, cBuf, iTop );   iTop = toStruct( 3, sWork, nn, cBuf, iTop );   nn++;  }  fclose( fp1 );  for( i = 0; i < nn; i++ ){   printf( "%-10s %2d %s\n", sWork[i].cName, sWork[i].iAge, sWork[i].cJob );  } } 注:インデントに全角空白を用いています。

HNDHDK
質問者

お礼

yama5140さん この間の質問といい、たびたびありがとうございます☆ サブ関を使ったやり方ですね☆ 参考にさせていただきます☆

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.3

>ageはint型なのでどのようにしたら? じゃあ、ひとまずコイツは list[i].age = 0; にしといて、コンパイルしてみよか。 エラーが出ると思うので strcpy のマニュアルなどみつつ修正して下さい。

HNDHDK
質問者

お礼

koko_u_さん 皆様の解説とを参考にしながら、ようやく表示に成功しました!! 理解力に乏しい自分に何度も解説していただき 本当にありがとうございましたm(_ _)m

HNDHDK
質問者

補足

なんとかコンパイルまでこぎつけました☆ 現段階でのプログラムはこんな感じなんですが、 表示がされなくて… while(fgets(string,PROF,fp)) { while(string!=" ") { strcpy(list[i].name,string); } while(string!=" ") { list[i].age=0; } while(string!='\0') { strcpy(list[i].male,string); } i++; } for(i=0;i<5;i++) { printf("%s %d %s\n",list[i].name,list[i].age,list[i].male); } ご指導お願いします!!!

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.2

>中の処理は、 > >strcpy(list[i].name,~); > > みたいなことこですか? ま、こういうのは「正解」はないので、 まずは自分の知っている関数を使ってとにかく動くコードを書きましょう。 strcpy はコピー元の文字列で '\0' が現れるまでをコピーしますね。 「田中」までをコピーするにはどうすればよいか考えましょう。 はい。補足にどうぞ。

HNDHDK
質問者

補足

while(fgets(string,128,fp)) { for(n=0;string[n]!=" ";n++) { strcpy(list[i].name,string[n]); } for(n=n+1;string[n]!=" ";n++) { list[i].age=; } for(n=n+1;string[n]!='\0';n++) { strcpy(list[i].name,string[n]); } } こんな感じでいいのでしょうか? ageはint型なのでどのようにしたら?

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.1

・ファイルから一行ずつ読み込むコードを書く。 ・読み込んだ一行を構造体のメンバーに合わせて分解するコードを書く。 はい、補足にどうぞ。

HNDHDK
質問者

補足

書けるかどうか…笑 while(fgets(string,128,fp)) { } すいませんfgetsで読み込むまでしか書けませんm(_ _)m 中の処理は、 strcpy(list[i].name,~); みたいなことこですか?

関連するQ&A

  • C言語 構造体配列 list

    プログラミングでこの問題がわからないので教えてください! 下記の構造体タグseisekiを使って,表に示すデータをもつ構造体配列listを作成する. 構造体配列listを利用して,一番点数の高い 者を探し,その名前を表示する. struct seiseki{ char name[30]; int score; name score }; 表 佐藤 80 鈴木 75 田中 95 高橋 90

  • ファイル読込時に構造体の文字列ポインタに割当てたいと

    ファイル読込時に構造体の文字列ポインタに割当てたいと思っています。 (new 演算子を使用します。) 文字列の長さが不定です。 どうすれば、文字列の長さを知ることができますか? 以下のようなところまでは作れましたが、 困っています。 void loaddata()のfscanf関数の部分です。 ほかにも関数の void outputdata() void deletedata() がありますが、長いので省略しました。 ********************************************************** #include<stdio.h> #include<string.h> class data { public: struct basic { char *name; int age; struct basic *next; }; private: struct basic *base; struct basic *base_top; int cnt; public: data::data() { cnt=0; } void inputdata(char *name,int age) { if(cnt==0) { base=new basic; base_top=base; base->age=age; int len=strlen(name); base->name=new char[len+1]; strcpy(base->name,name); cnt++; } else { base->next=new basic; base=base->next; base->age=age; int len=strlen(name); base->name=new char[len+1]; strcpy(base->name,name); cnt++; } } void savedata() { base=base_top; FILE *fp; fp=fopen("dat.txt","w"); for(int i=0;i<cnt;i++) { fprintf(fp,"%s\t%d\n",base->name,base->age); base=base->next; } fclose(fp); } void loaddata() { if(cnt!=0){deletedata();} cnt=0; FILE *fp; fp=fopen("dat.txt","r"); while(1) { fscanf(fp,"%s\t%d\n",base->name,base->age); } } };

  • ファイルから読みこんで構造体に格納する、

    shohinというファイルに RX-100 odango_tsumeawase 3000という のが 五行ならんでいるのですが、 これを読み込んで struct shohin{ char code[10]: char name[40]; int price; } にファイルから読みこんで構造体配列に 格納したいのですが、構造体配列に格納する やりかたがわかりません。 構造体配列は struct shohin list[];というのを宣言しています。 ファイルから一行読み込んで fprintf()を使おうと思うのですが、 それはできますか? メンバ毎に格納したいのですが、 それがわかりません お願いします。

  • 構造体のリストをソートしたい。

    ある名簿のリストを作りました。 以下のような構造体で、 typedef struct meibo{ char name[10]; int old; struct meibo *next; }MEIBO; これを、ポインタp->next->nameをたどっていって、名前が辞書順になるようにリストを作ったのですが、 これを年齢順にソートして表示させたいんです。 どんな方法があるんでしょうか? 一旦すべてを配列に格納して、クイックソート…とかも考えたのですが、すごく領域をとるし、なんか2度手間(最初から配列に順に格納していけばよかったなぁ・・・と。 それでもやっぱり最初から名列順にするときから配列に入れておくほうがいいのでしょうか? 教えてください。 (最初から年齢を比較してリストを作れば・・ってのはなしで、名列順のリストが存在するものとしてください。)

  • 構造体について

    以下のような構造体の宣言が合ったとき struct list { int a; char b[20]; struct list *next; }; struct list *add_list( int a, char *str, struct list *head );・・・1 struct list *del_list( int a, struct list *head );・・・2 1は引数の数が同じため問題ないのですが、2はどういう意味になるのでしょう?

  • C言語 list 構造体配列

    どうしてもこの問題がわからないので教えてください!  下記の構造体タグ「seiseki」を使って,表に示すデータをもつ構造体配列「list」を作成する.  関数に構造体配列「list」のアドレスを渡す.  関数で構造体ポインタ「*list」を利用して,一番点 数の高い者(1名限定)を調べ,その名前を表示する. struct seiseki{ char name[30]; int score; name score }; 表 佐藤 80 鈴木 75 田中 95 高橋 90

  • C言語_関数_構造体です!教えて下さい!!

    <C言語です> 新しく関数を宣言した中で構造体を使用したいのですが, 上手くいきません。 ご指導宜しくお願い致します!! /*data.csv*/ ------------ 鈴木,21 田中,22 佐藤,23 ------------ 以下プログラム ------------ #include <stdio.h> /*構造体の宣言*/ typedef struct list{ char name[80]; int age; }list1; /*関数の宣言*/ void kiroku(FILE *fo,int ki1){ if(ki1==1) fprintf(fo,"%sの歳は%dです。",data[ki1].name,data[ki1].age); else if(ki==2) fprintf(fo,"%sの歳は%dです。",data[ki2].name,data[ki2].age); else fprintf(fo,"%sの歳は%dです。",data[2].name,data[2].age); } int main(){ char *fin="data.csv"; char *fnmo="kiroku.txt"; FILE *fi,*fo; list1 data[10]; int i,k,r; char ss[80]; //open files. fi=fopen(fin,"r"); fo=fopen(fnmo,"w"); //read r=0; while(fscanf(fi,"%[^,],%d",ss,&k)!=EOF){ strcpy(data[r].name,ss); data[r].age=k; r++; } i=1; /*関数の実行*/ kiroku(fo,i); // close files. fclose(fi); fclose(fo); return 0; }

  • コンピュータープログラミングの質問です。

     下記の構造体タグ「seiseki」を使って,表に示すデータをもつ構造体配列「list」を作成。.  構造体配列「list」を利用して,一番点数の高い者を探し,その名前を表示するプログラミングの書き方、わからないです、よろしくおねがいします、、、、 Struct seikei{ Char name[30]; Int score[30]; }; name. Score 佐藤 80 鈴木 75 田中 95 高橋 90

  • 構造体から他の構造体への代入

    現在C言語で簡単なプログラミングを書いているのですが、 構造体(1)(下記参照)から、構造体(2)(下記参照)への代入の方法がわからず悩んでいます。もしよければ手を貸してください!! 標準関数などがないということはわかったので、 地道に代入を行いたいです。 イメージ・・・(data[n].b = moji[n].d;) (1)struct list_kouzou{ int a; char b[30]; char c[8]; }data[100]; (2)struct list_tai{ char d[30]; }moji[15];

  • プログラミング構造体について。

    include<stdio.h> #include<stdlib.h> #include<string.h> struct person{ char name[10]; int gender; int age; }; void printPersonList(struct person *person_p, int size); void outputPersonList(struct person *person_p, int size); double getAverageOfAge(struct person *person_p, int size); int countMales(struct person *person_p, int size); int countFemales(struct person *person_p, int size); int main(void){ struct person *person_p; int i, count, gender, age, maleCount, femaleCount; char name[20]; double average; printf("登録する人数を入力してください。\n"); scanf("%d", &count); person_p = (struct person*)malloc(sizeof(struct person)* count); for(i=0; i < count; i++){ printf("名前・性別(男性:0, 女性:1)・年齢をスペース区切りで入力してください。\n"); scanf("%s %d %d", name, &gender, &age); strcpy((person_p + i) -> name, name); (person_p + i) -> gender = gender; (person_p + i) -> age = age; } printPersonList(person_p, count); outputPersonList(person_p, count); average = getAverageOfAge(person_p, count); printf("平均年齢:%f\n", average); maleCount = countMales(person_p, count); femaleCount = countFemales(person_p, count); printf("男性:%d名, 女性:%d名\n", maleCount, femaleCount); free(person_p); return 1; } void printPersonList(struct person *person_p, int size){ int i; printf("登録リスト\n"); printf(" name | gender | age\n"); printf("----------+--------+-----\n"); for(i=0; i < size; i++){ printf("%10s | %1d | %2d\n", (person_p + i) -> name, (person_p + i) -> gender, (person_p + i) -> age); } } void outputPersonList(struct person *person_p, int size){ FILE *output; int i; if((output = fopen("meibo.c", "w")) == NULL){ printf("meibo.cを開けませんでした。\n"); return; } for(i=0; i < size; i++){ fprintf(output, "%s, %d, %d\n", (person_p + i) -> name, (person_p + i) -> gender, (person_p + i) -> age); } fclose(output); } 残り3つの関数をすべて定義する(それぞれ10行程度) getAverageOfAge, countMales, countFemales どう定義すればいいのか教えてください。お願いします。