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

こんにちは。私は30代の男性です。
「名前」「身長」「体重」が記載されたCSVファイルの内容を読み取って、構造体の「name」「height」「weight」に格納するプログラムを作っています。CSVの内容は
A,175,80
B,167,89
C,155,45



Z,188,70
だと仮定します。数値が読み取れているか、下記のように「tp = strtok(file_image, ",\n" );」の前後に「printf("%s\n", file_image);」を置いてみたら、strtok前では全て表示されるのに、strtok後では「ABC」しか表示されません。これでは全てのデータを構造体に格納できないので、困っています。
1.どのようにすれば、数字も取り出せる(読み取れる)でしょうか?
2.効率よく構造体に格納するには、どのようにしたらよいでしょうか?

アドバイスを頂ければ幸いです。宜しくお願いいたします。

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
int main(int argc, char *argv[])
{
FILE *fp = NULL;
int rtn = 0;
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("ファイルオープンに失敗しました。\n");
return 1;
}
if (argc != 2)
{
printf("ERROR: オプションの数に過不足があります。\n");
return 1;
}
rtn = change_csv(fp);
return 0;
}
int change_csv(FILE *fp)
{
int i;
int j;
char file_image[256]; /* 読み込んだ先のメモリの領域 */
char *tp;
for (i = 0; i <= 256; i++)
{
if (fgets(file_image, 256, fp) == NULL)
{
if (ferror(fp) != 0)
{
printf("ERROR: 読み込みに失敗しました。\n");
return 1;
}
}
if (feof(fp) != 0)
{
break;
}
printf("%s\n", file_image);
tp = strtok(file_image, ",\n" );
printf("%s\n", file_image);
}
fclose(fp);
return 0;
}

A 回答 (3件)

★過去に似たような質問がありました。


・その質問は『改ページ』で区切られた文字列を読み込むにはどうすればよいか?
 というものでした。今回は CSV ですが『改ページ』の部分を『カンマ文字』に
 指定すれば文字列を分割できます。
・そのほか細かい点をアドバイスしますと
 (1)main 関数の『FILE *fp = NULL;』や『int rtn = 0;』は代入後に参照しているため初期化の必要はありません。
 (2)main 関数の『if (argc != 2){ … }』のリターンの前で『fclose(fp);』を記述しておきましょう。
 (3)change_csv 関数に『fclose(fp);』がありますが、main 関数で統一させると分かりやすくなります。
 (4)change_csv 関数で『fgets』のバッファ容量は『fgets(file_image,sizeof(file_image),fp)』とすると良いでしょう。
 (5)change_csv 関数で『feof』を使うのならば『i』カウンタは必要ない気がします。
  while ( fgets(file_image,sizeof(file_image),fp) != NULL ){
   /*
   行単位で CSV データを分割して構造体にセットする処理
   */
  }
  if ( feof(fp) ){
   printf( "SUCCESS: 正常に読み込みました。\n" );
  }
  else if ( ferror(fp) ){
   printf( "ERROR: 読み込みに失敗しました。\n" );
  }
  else{
   printf( "ERROR: 不明なエラーで読み込みを中止しました。\n" );
  }
 (6)『strtok』を使うのなら次のようにします。→分かりやすくするためループしない記述です。
  char *array[ 3 ];
  char *token = " ,\t\r\n";
  
  array[ 0 ] = strtok( file_image, token );…名前(name)
  array[ 1 ] = strtok( NULL, token );……身長(height)
  array[ 2 ] = strtok( NULL, token );……体重(weight)

最後に:
・『strtok』もよいがカンマ文字の前後にタブやスペースがないならば『strchr』関数でも分割できます。
 その方法が下の『参考URL』でサンプルを載せています。どうぞ参考に。
・以上。頑張って下さい。私も30代の男性です。

参考URL:http://oshiete1.goo.ne.jp/qa2749340.html
    • good
    • 0
この回答へのお礼

Oh-Orange様
毎回ご回答頂き、ありがとうございます。(1)~(6)までのアドバイスを反映させて頂きます。Oh-Orange様も30代でしたか。私は最近C言語の勉強を始めたばかりです。
またの機会にアドバイスを頂ければ幸いです。
今後とも宜しくお願いいたします。

お礼日時:2007/04/02 10:50

CSVファイルの1行のデータの並びがすでに決まっているのなら、


fgets で1行分読み込んで sscanf で一度に各要素に分解するのが
簡単だと思います。同時に書式チェックもできますし。

質問の例で書くと、1行分の取得は、名前が40バイトまでとするなら

char name[41],dummy[2];
int height, weight;

// 1行読み込み(ファイル終了/エラー処理は省略)
fgets(file_image, sizeof(file_image), fp);

// 各要素に分解して取得&同時に書式チェック
if (sscanf(file_image,"%40[^,],%d,%d%1s",name,&height,&weight,dummy) == 3) {
 // 正常に取得されたときの処理
 ...
} else {
 // 書式エラー時の処理
 ...
}

ただし、文字列要素にカンマが含まれててその文字列が引用符で
囲まれてるようなCSVなど、複雑な場合は簡単に済ませるわけには
いきませんが。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

>sscanf で一度に各要素に分解するのが簡単だと思います。同時に書式チェックもできますし。

そのような機能もあったのですね。勉強になります。
ありがとうございました。

お礼日時:2007/04/02 10:45

> strtok前では全て表示されるのに、strtok後では「ABC」しか表示されません。


strtokはそのような動作をします。strtokは対象文字列を書き換えますから。
> これでは全てのデータを構造体に格納できないので、困っています。
そんなことはありません。strtokの使い方をちゃんと調べましたか?
http://www9.plala.or.jp/sgwr-t/lib/strtok.html

strtokでのトークン分割動作を確認したいなら、
> tp = strtok(file_image, ",\n" );
> printf("%s\n", file_image);
を以下のように書き換えてください。
 for(tp = strtok(file_image, ",\n" ); tp != NULL; tp = strtok(NULL, ",\n" ) ){
   printf("%s\n", tp);
 }



それと、main関数でのargcのチェックはfopenの前にやった方が良いのでは。
あと、fopenとfcloseが別の関数にあるのはちょっと気持ち悪いですね。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

>それと、main関数でのargcのチェックはfopenの前にやった方が良いのでは。
>あと、fopenとfcloseが別の関数にあるのはちょっと気持ち悪いですね。

なるほど。言われてみればそうですね。勉強になります。そのように致します。

お礼日時:2007/04/02 10:43

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