プロが教えるわが家の防犯対策術!

こんばんは☆

Cのプログラミングに困っています(>_<)

テキストファイル(meibo.txt)
田中 32 公務員
佐藤 20 学生

というファイルを読み込んで、構造体に入れて表示するには
どうしたらよいでしょうか?

struct meibo{
    char name[20];
    int age;
    char job[20];
}list[3];

としたあとにどのように入れたらよいかがわかりません…
わかる方教えてくださいm(_ _)m

A 回答 (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()関数で行います。

この回答への補足

Oh-Orangeさん

>list[ i ].age *= 10;
>list[ i ].age += (string[n] - '0'); ←文字から数値に変換

の2行が理解しがたいのですが…これは、
決まりみたいなものなのでしょうか?
特に上の行の10を代入の意味は???

補足日時:2008/02/05 17:03
    • good
    • 0

★回答者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() 関数を
使って処理させる方法もあります。いろいろと工夫してみてください。
    • good
    • 0
この回答へのお礼

>Oh-Orangeさん

本当に丁寧な解説ありがとうございました☆☆☆
おかげでこのやり方でもうまく表示できました(^o^)

Oh-Orangeさんのお陰で、
文字列の処理(入門)の仕方が
かなりわかりました(^o^)b
今度は自分の文字列処理の仕方を
見つけて、知識を深めたいと思います☆

ありがとうございました☆

お礼日時:2008/02/06 19:30

★回答者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;
}

補足日時:2008/02/06 16:12
    • good
    • 0

初心者向きなら


以下のような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の使用は嫌うようです。
    • good
    • 0
この回答へのお礼

gyrocompasさん

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

お礼日時:2008/02/05 17:09

> while(string!=" ")



これは、ローカル変数のstringの「アドレス」と、プログラム中のどこかに確保された
" "(メモリ上では 0x20 0x00)の「アドレス」を比較しています。
結果が等しくない時に真となりますので、ここの条件判定は常に真です。
# 値の書き換わる変数と、文字列定数の先頭アドレスが同一になることは絶対にありませんので。

表示されないのは、
strcpy(list[i].name,string);
を無限ループで実行しているため、ここから先に進まないからではないでしょうか。

文字列の比較はstrcmp()を使用しましょう。

# C++の文字列クラスの場合、string!=" "と言う判定が可能になっている場合がありますが。
    • good
    • 0
この回答へのお礼

Wr5さん

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

お礼日時:2008/02/05 17:02

★デリミタは、半角空白(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 );
 }
}
注:インデントに全角空白を用いています。
    • good
    • 0
この回答へのお礼

yama5140さん

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

お礼日時:2008/02/05 16:59

>ageはint型なのでどのようにしたら?


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

この回答への補足

なんとかコンパイルまでこぎつけました☆

現段階でのプログラムはこんな感じなんですが、
表示がされなくて…

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);
}

ご指導お願いします!!!

補足日時:2008/02/04 23:57
    • good
    • 0
この回答へのお礼

koko_u_さん

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

お礼日時:2008/02/05 17:18

>中の処理は、


>
>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型なのでどのようにしたら?

補足日時:2008/02/04 23:07
    • good
    • 0

・ファイルから一行ずつ読み込むコードを書く。


・読み込んだ一行を構造体のメンバーに合わせて分解するコードを書く。

はい、補足にどうぞ。

この回答への補足

書けるかどうか…笑

while(fgets(string,128,fp))
{
}

すいませんfgetsで読み込むまでしか書けませんm(_ _)m
中の処理は、

strcpy(list[i].name,~);

みたいなことこですか?

補足日時:2008/02/04 22:34
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!