- ベストアンサー
CSVファイルの内容を構造体に格納したい(Unix使用)。
こんにちは。私は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; }
- みんなの回答 (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代の男性です。
その他の回答 (2)
- tea_sheep
- ベストアンサー率53% (8/15)
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など、複雑な場合は簡単に済ませるわけには いきませんが。
お礼
ご回答ありがとうございます。 >sscanf で一度に各要素に分解するのが簡単だと思います。同時に書式チェックもできますし。 そのような機能もあったのですね。勉強になります。 ありがとうございました。
- Werner
- ベストアンサー率53% (395/735)
> 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が別の関数にあるのはちょっと気持ち悪いですね。
お礼
ご回答ありがとうございます。 >それと、main関数でのargcのチェックはfopenの前にやった方が良いのでは。 >あと、fopenとfcloseが別の関数にあるのはちょっと気持ち悪いですね。 なるほど。言われてみればそうですね。勉強になります。そのように致します。
お礼
Oh-Orange様 毎回ご回答頂き、ありがとうございます。(1)~(6)までのアドバイスを反映させて頂きます。Oh-Orange様も30代でしたか。私は最近C言語の勉強を始めたばかりです。 またの機会にアドバイスを頂ければ幸いです。 今後とも宜しくお願いいたします。