プロが教える店舗&オフィスのセキュリティ対策術

はじめまして、数ヶ月前からC言語を始めたものです。

ファイルからのデータ読み込みについて質問です。
テキストファイルとして以下のようなものがあります。
*************************
23 3346 45 423 34
23 345 45 56 6345
・・・
・・・
*************************
このようなファイルから読み込みを行って、スペース毎の数字(整数型)を構造体の中の変数に順次格納していくようなプログラムを作りたいとおもうのですがどのように行ったらいいのでしょうか?

現在、fgets()で行ごとの文字列を読み込んでから、型変換を行おうと心みているのですが、うまくいきません。

回答よろしくお願いします。

A 回答 (4件)

・サンプル1(直接読み込み)


 #include<stdio.h>
 …
 int a[5];
 FILE *fp;
 …
 while(fscanf(fp,"%d%d%d%d%d",&a[0],&a[1],&a[2],&a[3],&a[4]) != EOF){
   代入処理
 }
 scanfは空白や改行文字で、数の区切りを見つけ、自動的にスキップしてくれます。

・サンプル2(fgetsを用いる場合)
 #include<stdio.h>
 …
 int a[5];
 FILE *fp;
 char buf[256];
 …
 while(fgets(buf,sizeof(buf),fp) != NULL){
  sscanf(buf,"%d%d%d%d%d",&a[0],&a[1],&a[2],&a[3],&a[4],&a[5]);
   代入処理
 }
 sscanfを用いて、文字列から各数字を読み取ります。

・サンプル3(1文字ずつ入力)
 #include<stdio.h>
 #include<stdlib.h>
 #include<ctype.h>
 …
 int c;
 int i,a[5],counter;
 char buf[32];
 FILE *fp;
 …
 counter = 0;
 while(1){
  while(!isgraph((c = fgetc(fp))) && c != EOF){
   /*区切り文字のスキップ*/
      ;
  }
  if(c == EOF)
   break;

  i=0;
  do{
   buf[i++] = c;
  }while(isdigit((c = fgetc(fp))));
  buf[i] = '\0';
  a[counter++] = atoi(buf);

  if(counter>=5){
   counter=0;

   代入処理
  }
  if(c==EOF)
   break;
 }

 1文字ずつ読み込んで、処理していきます。
 fgetsだと領域の大きさがデータに対して少なすぎると、数字が途切れてしまう可能性があります。
 極端な例ですと、fgets(buf,8,fp)に対し、「44 28 312…」などです。
 そのため、fgetsは確実に行入力するとは限らないことに注意しておかないといけません。
 isdigitではなく、isgraphを用いたのは、負の整数の場合に'-'も読み取るようにするためです。数字と'-'だけと判断すればよいのですが、ここでは妥協しました。
 1行の個数が固定でない場合は、if(counter>=5)をif(c=='\n')とすれば、対応することが出来ます。ですが、配列の大きさを超えないようにチェックする記述も必要となります。

 あと、ちょっとしたことですがfgetcの返り値はint型で受け取るべきであることを忘れないように。

 分からない関数があれば、調べてみてください。標準関数なので、探せば色々見つかると思います。
 初めは慣れないやり方や記述があるでしょうが頑張ってくださいね。長くなり読みづらそうですみません。
    • good
    • 0
この回答へのお礼

丁寧な解説ありがとうございました。
おかげで、プログラムを完成させることができました。

複数のやり方を教えていただいたことはとても勉強になりました。これからもプログラミングの勉強をがんばっていきたいとおもいます。また何かありましたらよろしくお願いします。

本当にありがとうございました。

お礼日時:2007/02/09 18:56

★回答者 No.1 です。

修正と新しい回答です。
・前回のサンプルの『fgets』の第3引数は『stdin』は『fp』が正しく、
 『scanf』は『fscanf』にすべきでした。つまり、
 『while ( fgets(buff,sizeof(buff),fp) != NULL ){』と
 『fscanf( fp, "%d %d %d %d %d\n", &a, &b, &c, &d, &e );』が
 正しい修正版です。申し訳ありません。→『fp』がファイルポインタです。
・akoyagai さん、ご指摘ありがとうございます。

別の回答:
・今度は、1行のデータが5つ以外の場合です。→最大 100 の要素に対応。
・文字列を左から順番に解読して、数字文字列を整数値に変換します。
・下にサンプルを載せます。→ポインタを使っていますよ。

サンプル:
int pos, num[ 100 ]; ←要素数
char buff[ 256 ];
char *seek; ←ポインタです。

(オープン処理)
 :
 while ( fgets(buff,sizeof(buff),fp) != NULL ){ ←1行ずつ読み込む
  for ( pos = 0, seek = buff ; *seek != '\0' ; pos++ ){ ←文字列を左から右へ解読する
   while ( isspace(*seek) ){ ←空白文字をスキップ
    seek++;
   }
   if ( pos >= 100 ){ ←データ要素が 100 を越えたので強制的にループを抜ける
    break;
   }
   num[ pos ] = atoi( seek ); ←数字文字列を整数に変換する(英字の場合は 0 をセット)
   
   while ( !isspace(*seek) && (*seek != '\0') ){ ←空白文字以外をスキップ(数字,英字,記号など)
    seek++;
   }
  }
  data.array[ 0 ] = num[ 0 ]; // data が構造体、array[] がメンバです
  data.array[ 1 ] = num[ 1 ]; // num が1行のデータ要素(1-100)です
  data.array[ 2 ] = num[ 2 ];
  data.array[ 3 ] = num[ 3 ];
  data.array[ 4 ] = num[ 4 ];
 }
 :
(クローズ処理)

最後に:
・上記のサンプルの『fp』がファイル・ポインタです。
・『isspace』を使うために『#include <ctype.h>』をインクルードして下さい。
・『atoi』関数を使うために『#include <stdlib.h>』もインクルードして下さい。
・サンプル・ソースを紹介しましたが、構造体メンバがはっきりしないので正確なアドバイスなどは
 これ以上で来ません。あくまで参考として下さい。
・以上。おわり。
    • good
    • 0
この回答へのお礼

丁寧な説明ありがとうございました。
おかげでプログラムを完成させることができました。

ひとつのやり方だけでなく、別のやり方でのやり方を教えていただいたことは、よい勉強になりました。これからも、プログラミングをがんばっていきます。

本当にありがとうございました。

お礼日時:2007/02/09 18:54

5つ数字があると決まっている場合は、ファイルポインタをfpとすると、


fscanf(fp,"%d %d %d %d %d\n",&a[0],&a[1],&a[2],&a[3],&a[4]);
という感じで、後はNo.1の方のようにすればいいと思います。
(No.1の方は標準入力からの入力と勘違いされている気がします)

また、そうと決まっていない場合でも、
char temp[10];
//一時的に確保します
char c;
//getcの格納先
int a[100];
//数字の格納先
memset(temp,0,sizeof(temp));
//初期化
int i=0,j=0;
//iはtempが何文字あるか、jはaがどの要素まで格納されているか
whlie((c=getc(fp))!=EOF){
//ファイルの終わりまできたら終了
if((c!=' ')&&(c!='\n')){
//スペースや改行で無いのならtempにcを足していきます
temp[i]=c;
i++;
}
else{
//スペースや改行なら、そこでtempを数字の型に変換します
a[j]=atoi(temp);
memset(temp,0,sizeof(temp));
i=0;
//初期化です
j++;
//aの次の要素に格納できるように
}
}

実際に試してはいませんが、たぶんいけると思います。(ごめんなさい、見にくかも)
もし、うまくいかないとか、説明の意味がわからないとかがあれば、また尋ねてください。
    • good
    • 0
この回答へのお礼

丁寧な説明ありがとうございました。
おかげでプログラムが完成しました。

ひとつのやり方だけでなく別のやり方も教えていただき、理解が深まったと思います。
また何かあったらよろしくお願いします。

本当に助かりました、ありがとうございます。

お礼日時:2007/02/09 18:51

★『scanf』関数で簡単に出来ると思います。


・1行のデータが必ず5つの数字があるの場合は、
 『scanf( "%d %d %d %d %d\n", &a, &b, &c, &d, &e );』で簡単に型変換できると思います。
 これで、『a』~『e』の変数を構造体にセットするか、直接構造体メンバを『scanf』の引数に
 アドレス指定で渡します。

サンプル:
char buff[ 256 ];
int a, b, c, d, e;

(オープン処理)
 :
 while ( fgets(buff,sizeof(buff),stdin) != NULL ){
  scanf( "%d %d %d %d %d\n", &a, &b, &c, &d, &e );
  
  data.array[ 0 ] = a; // data が構造体、array[] がメンバです
  data.array[ 1 ] = b;
  data.array[ 2 ] = c;
  data.array[ 3 ] = d;
  data.array[ 4 ] = e;
 }
 :
(クローズ処理)

最後に:
・1行のデータが5つ以外の場合は、文字列を左から右へサーチして数字を整数値に変換します。
・文字列を整数に変換するには『atoi』関数を使います。また、文字列のサーチ移動はポインタを
 使うと簡単ですが、C言語を始めたばかりならば『添え字』を使った方法でも良いでしょう。
・まずは C言語での『文字列とは』を理解しましょう。C言語では char 型の配列で文字列を表現
 して、最後は必ず NULL 文字をセットします。この NULL 文字が文字列の終端を表します。
・以上。おわり。→上記はエラーチェックは省略しています。

参考URL:http://www9.plala.or.jp/sgwr-t/c/sec05.html
    • good
    • 0

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