- ベストアンサー
型によらないCSVファイルの読み込みC言語プログラム
- N行*M列のエクセルデータをCSV形式に変換し、C言語で読み込むプログラムを作成しました。
- NとMの値に関わらず、任意のサイズのCSVデータを読み込むことができます。
- CSVデータを読み込み、配列に保存して出力するC言語プログラムです。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
>あらかじめ型のわかっているN行*M列のエクセルデータをCSV形式にし、読み込むプログラムを作成しました。 >これをNとMがどんな値であれ読み込めるようにするにはどうすれいいでしょうか 要するに、N行M列のCSVファイルをフリーの動的サイズで処理したいということですね? 試験的にSCVファイルで一度実験してみて行数Nが合わないようでしたら SAFTY を+5行とか+10行とかで補正してください。 /* Sample program ----- 実行形式 ----- ./a.out file_name */ #include <stdio.h> #include <stdlib.h> /* exit() */ #include <errno.h> /* errno */ #define SIZE 256 #define SAFTY 0 /* もしもの行数をこれで補正 >5 */ int main(int argc, char *argv[]) { char line[SIZE],*p; int M, N; FILE *fp; if(argc != 2) return 0; if((fp = fopen(argv[1],"r")) == NULL){ fprintf(stderr, "%s\n", strerror(errno)); exit(EXIT_FAILURE); } /* 取り合えず、1行読み込み */ fgets(line, SIZE, fp); /* カンマ(,)の数を列数M 数える */ M = 1; p = line; while(*p){ if(*p++ == ',') M++; } printf("M= %d : %s", M, line); /* シリンダーヘッドを末尾に移動し、行数N を求める */ fseek(fp, 0, SEEK_END); N = ftell(fp) / (p - line) + SAFTY; printf("N= %d\n", N); /* ここで malloc()を使う */ //csvdata = (double(*)[M])malloc(sizeof(double[M]) * N); /* シリンダーヘッドを最初に戻し */ fseek(fp, 0, SEEK_SET); /* 以下、通常のCSVファイル処理を継続する(省略してます)。*/ while(fgets(line, SIZE, fp) != NULL){ printf("%s", line); } fclose(fp); return 0; }
その他の回答 (6)
- yama5140
- ベストアンサー率54% (136/250)
#4 です。 >実行してみましたが、動きませんでした >>大きくは、冒頭の3行を直すだけで、「実際にいれる」ことができ まさか、本当に3行を直した「だけ」で実行したのですか。 #3 のソースをそのまま実行してみて下さい。 データ配列が異なるので、思いどおりに出力しないのは当たり前ですが、「動く」はずです。 (再度動作確認済み) >おそらく勘違いされていると思うのですが、 >NとMの値はcsvの行・列数にしたいので、 >defineで決めるのがそもそもおかしい気がするのですが 「NとMの値はcsvの行・列数」のあたりは、いじってませんし、 私が「defineで決める」ようにしたわけでもありません。 勘違いのしようがないのですが・・。 もし、勘違いしてたら、#3 で >>(確認済み) とはできませんよ。ダミーデータすらできないのだから。また、 >>なお、デバック段階では、N と M は下のように小さくし、ダミーデータもそれに揃えた方が楽です。 「NとMの値はcsvの行・列数」と認識しているからの記述です。
お礼
ありがとうございました
No.2です。 >ほとんど理解できていません 正直、何を目指しているのか理解できません。 もし、宿題などであれば、理解もしないで答えだけ提出してもすぐにバレてしまいますし、今後を考えれば難しくなればなるほど、自分では解決できなくなってしまいます。 差し出がましいようですが、実行環境があるようなので、1ステップずつ実行してみたり、あるいはprintf()などで値を確認するなどして、理解された方がいいでしょう。 No.3さんより >メモリ確保関数を用いるより、変数の宣言のほうが楽です。 この言葉どおりで、まずは大きめに領域を確保しておき、それに代入するのがいいでしょう。 たとえば、N行=1000,M列=100ぐらいで。 また、静的(static double csvdata[N][M];)に領域を確保した方が安心安全です。 ソースを見ると、確かにポインターを使って難しそうに見えます。 しかし、やっていることは、1行読み込んでそれからカンマ区切りを見つけながら、浮動小数点文字列をdouble値に変換し、2次元配列に代入しているだけです。
- yama5140
- ベストアンサー率54% (136/250)
#3 です。 >今は環境がないので実行できませんが、 ★うぅ~、無銭飲食か~。 >これだと3行2列の形式にしか対応してないんじゃないですか? >>なお、デバック段階では、N と M は下のように小さくし、 >これはコピペした時に空白が消えてしまっただけで、 ★ならば正常に動きます。 >これをNとMがどんな値であれ読み込めるようにするにはどうすれいいでしょうか >>N と M に変更があれば、コンパイルし直さなければなりません。
補足
実行してみましたが、動きませんでした おそらく勘違いされていると思うのですが、 NとMの値はcsvの行・列数にしたいので、 defineで決めるのがそもそもおかしい気がするのですが
- yama5140
- ベストアンサー率54% (136/250)
>・・既存のプログラムは少しいじっただけなのでほとんど理解できていません >実際にいれる文が欲しいです コンパイルしてみれば、少しは理解できると思います。 大きくは、冒頭の3行を直すだけで、「実際にいれる」ことができました(確認済み)。 ★要は、#define N23 と、#define N 23 は大違い、ということです。 +++++++++++++++ 以降、蛇足 ++++++++++++++++++++++ >型っていうのはMとNの値です N と M に変更があれば、コンパイルし直さなければなりません。 このようなプログラムでは、(コンパイルし直す必要のない動的な)メモリ確保関数を用いるより、変数の宣言(◆)のほうが楽です。 http://homepage3.nifty.com/mmgames/c_guide/q_malloc.html なお、デバック段階では、N と M は下のように小さくし、ダミーデータもそれに揃えた方が楽です。 以上をふまえたものを示します、参考にして下さい。 大きな変更 // ******* の付いた行 追加 // ++++++++ の付いた行 (超蛇足:C++記述を、C記述へ直しちゃいました) #include <stdio.h> #include <stdlib.h> #include <string.h> #define N 3 // ******* #define M 2 // ******* #define SEP_DATA ',' // ******* int csv_read( char filename[], double csv[N][M] ) { FILE *fp; char line[256], *ptr; int i = 0, j; if( ( fp = fopen( filename, "r" ) ) == NULL ){ printf( " file open error!!\n" ); return -1; } while( fgets( line, 256, fp ) != NULL ){ printf( "*%s", line ); ptr = line; j = 0; do{ csv[i][j] = atof( ptr ); ptr = strchr( ptr, SEP_DATA ); if( ptr != NULL ){ ptr++; } j++; }while( ptr != NULL && j < M ); i++; if( N <= i ) break; // ++++++++ } fclose( fp ); return 0; } int main( int argv, char *argc[] ) { int i, j; char filename[256]; double csvdata[ N ][ M ]; // ◆ ++++++++ if( argv > 1 ){ strcpy( filename, argc[1] ); }else{ printf( "Please Input Filename:" ); scanf( "%s", filename ); } for( i = 0; i < N; i++ ){ for( j = 0; j < M; j++ ){ csvdata[i][j] = 0.0; } } if( csv_read( filename, csvdata ) < 0 ) return -1; for( i = 0; i < N; i++ ){ printf( "%f", csvdata[i][0] ); // *** %lf for( j = 1; j < M; j++ ){ printf( "\t%f", csvdata[i][j] ); // *** %lf } printf( "\n" ); } return 0; } 注:インデントに全角空白を用いています。コピペ後、タブに一括変換して下さい。
補足
今は環境がないので実行できませんが、 これだと3行2列の形式にしか対応してないんじゃないですか? >#define N23 と、#define N 23 これはコピペした時に空白が消えてしまっただけで、実際には右側のように書いてあります
ここまでできているのなら最初に1行読み込んで、列数を求め、以降はrealloc()で1行読むたびにメモリを拡張していけばいいでしょう。 でも >型によらないCSVファイル… この「型」って?
補足
型っていうのはMとNの値です 適切な言葉がわかりませんでした プログラムを作成した、 とは言っても既存のプログラムは少しいじっただけなので ほとんど理解できていません 実際にいれる文が欲しいです
- tsukasa-12r
- ベストアンサー率65% (358/549)
方法A (1) とりあえず、一行読み込んで列数をカウント (2) 一旦、行数カウントのためだけにファイルを読み込んで行数をカウント (3) 必要なメモリを確保 (4) 今までと同じ流れでファイルの先頭から読み込み 方法B とりあえず、一行読み込んで列数をカウント 一行分のデータを 次の行へのポインタ+配列 の線形リストにして、一行一行、 メモリ確保 → ファイルから一行読み込み → 確保したメモリにデータをセット とする。 二次元配列でなければならないのであれば、一旦、線形リストの形で読み込んでおいて、 全行読み込んだ後でリストの先頭からたどって二次元配列にセットしなおすとか。 線形リストについては、 http://www.cc.kyoto-su.ac.jp/~yamada/ap/list.html とか、検索するといろいろ出てくると思います。
補足
プログラムを作成した、 とは言っても既存のプログラムは少しいじっただけなので ほとんど理解できていません 実際にいれる文が欲しいです
お礼
ありがとうございました
補足
実行してみましたが、何も表示されず処理が終わってしまいました。 C++として実行していますので、 #include <string.h> を追記しました