- ベストアンサー
ファイルの読み込みと構造体
こんばんは☆ Cのプログラミングに困っています(>_<) テキストファイル(meibo.txt) 田中 32 公務員 佐藤 20 学生 というファイルを読み込んで、構造体に入れて表示するには どうしたらよいでしょうか? struct meibo{ char name[20]; int age; char job[20]; }list[3]; としたあとにどのように入れたらよいかがわかりません… わかる方教えてくださいm(_ _)m
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
★全く分からんのですか? >としたあとにどのように入れたらよいかがわかりません… ↑ これは文字列の表現の仕組みを理解していれば出来ると思います。 回答者 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()関数で行います。
その他の回答 (8)
- Oh-Orange
- ベストアンサー率63% (854/1345)
★回答者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() 関数を 使って処理させる方法もあります。いろいろと工夫してみてください。
お礼
>Oh-Orangeさん 本当に丁寧な解説ありがとうございました☆☆☆ おかげでこのやり方でもうまく表示できました(^o^) Oh-Orangeさんのお陰で、 文字列の処理(入門)の仕方が かなりわかりました(^o^)b 今度は自分の文字列処理の仕方を 見つけて、知識を深めたいと思います☆ ありがとうございました☆
- Oh-Orange
- ベストアンサー率63% (854/1345)
★回答者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 がセットされます。 この仕組みが『文字列から整数値の変換』の決まりみたいな方法です。 ・以上。
補足
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; }
- gyrocompas
- ベストアンサー率23% (24/104)
初心者向きなら 以下のような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の使用は嫌うようです。
お礼
gyrocompasさん 初心者なので、このような解説もとてもありがたいです☆ シンプルなのですぐに組み込んでみたいと思います☆
- Wr5
- ベストアンサー率53% (2173/4061)
> while(string!=" ") これは、ローカル変数のstringの「アドレス」と、プログラム中のどこかに確保された " "(メモリ上では 0x20 0x00)の「アドレス」を比較しています。 結果が等しくない時に真となりますので、ここの条件判定は常に真です。 # 値の書き換わる変数と、文字列定数の先頭アドレスが同一になることは絶対にありませんので。 表示されないのは、 strcpy(list[i].name,string); を無限ループで実行しているため、ここから先に進まないからではないでしょうか。 文字列の比較はstrcmp()を使用しましょう。 # C++の文字列クラスの場合、string!=" "と言う判定が可能になっている場合がありますが。
お礼
Wr5さん なるほど☆ どおりで継続条件がうまくいかないわけですね… わかりやすい解説をありがとうございます☆
- yama5140
- ベストアンサー率54% (136/250)
★デリミタは、半角空白(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 ); } } 注:インデントに全角空白を用いています。
お礼
yama5140さん この間の質問といい、たびたびありがとうございます☆ サブ関を使ったやり方ですね☆ 参考にさせていただきます☆
- koko_u_
- ベストアンサー率18% (459/2509)
>ageはint型なのでどのようにしたら? じゃあ、ひとまずコイツは list[i].age = 0; にしといて、コンパイルしてみよか。 エラーが出ると思うので strcpy のマニュアルなどみつつ修正して下さい。
お礼
koko_u_さん 皆様の解説とを参考にしながら、ようやく表示に成功しました!! 理解力に乏しい自分に何度も解説していただき 本当にありがとうございましたm(_ _)m
補足
なんとかコンパイルまでこぎつけました☆ 現段階でのプログラムはこんな感じなんですが、 表示がされなくて… 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)
>中の処理は、 > >strcpy(list[i].name,~); > > みたいなことこですか? ま、こういうのは「正解」はないので、 まずは自分の知っている関数を使ってとにかく動くコードを書きましょう。 strcpy はコピー元の文字列で '\0' が現れるまでをコピーしますね。 「田中」までをコピーするにはどうすればよいか考えましょう。 はい。補足にどうぞ。
補足
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)
・ファイルから一行ずつ読み込むコードを書く。 ・読み込んだ一行を構造体のメンバーに合わせて分解するコードを書く。 はい、補足にどうぞ。
補足
書けるかどうか…笑 while(fgets(string,128,fp)) { } すいませんfgetsで読み込むまでしか書けませんm(_ _)m 中の処理は、 strcpy(list[i].name,~); みたいなことこですか?
補足
Oh-Orangeさん >list[ i ].age *= 10; >list[ i ].age += (string[n] - '0'); ←文字から数値に変換 の2行が理解しがたいのですが…これは、 決まりみたいなものなのでしょうか? 特に上の行の10を代入の意味は???