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

あらかじめ型のわかっているN行*M列のエクセルデータをCSV形式にし、
読み込むプログラムを作成しました。
そのプログラムを下に示します

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 確保するデータ保存領域の大きさ(N行×M列) */
#define N23
#define M6

/* データの区切り文字 */
#define SEP_DATA','

int csv_read(char filename[], double csv[N][M]) {
/* ファイルオープン */
FILE *fp;
if( (fp = fopen(filename, "r")) == NULL ) {
printf(" file open error!!\n");
return -1;
}

/* 1行毎に読み出し */
char line[256], *ptr;
int i, j, k;
i=0;
while (fgets(line, 256, fp) != NULL) {
printf("*%s", line);
ptr = line; j=0;
do{
/* line[j]から次のタブ文字までを数値に変換 */
csv[i][j] = atof(ptr);
/* 次のタブ文字の位置を探す */
ptr = strchr(ptr, SEP_DATA);
/* タブ文字の次の文字を示す */
if (ptr!=NULL) { ptr++; }
j++;
}while(ptr!=NULL && j<M);
i++;
}

/* ファイルクローズ */
fclose(fp);
return 0;
}

int main(int argv, char *argc[]) {
char filename[256];
if( argv > 1){
strcpy(filename, argc[1]);
} else {
printf("Please Input Filename:");
scanf("%s", filename);
}

/* データ保存用の領域を確保 */
double (*csvdata)[M];
csvdata = (double(*)[M])malloc(sizeof(double[M]) * N);
//malloc(sizeof(*csvdata) * N);
if ( csvdata == NULL ){
return -1;
}
int i,j;
/* 配列の初期化 */
for( i=0; i<N; i++) {
for( j=0; j<M; j++) {
csvdata[i][j] = 0.0;
}
}

/* CSVデータの読み込み */
if( csv_read(filename, csvdata) < 0 ) {
return -1;
}

/* 配列の出力 */
for( i=0; i<N; i++) {
printf("%lf", csvdata[i][0]);
for( j=1; j<M; j++) {
printf("\t%lf", csvdata[i][j]);
}
printf("\n");
}
free(csvdata);
csvdata = NULL;
return 0;
}



これをNとMがどんな値であれ読み込めるようにするにはどうすれいいでしょうか

A 回答 (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;
}

この回答への補足

実行してみましたが、何も表示されず処理が終わってしまいました。
C++として実行していますので、
#include <string.h>
を追記しました

補足日時:2011/02/11 21:13
    • good
    • 0
この回答へのお礼

ありがとうございました

お礼日時:2011/03/24 07:13

#4 です。



>実行してみましたが、動きませんでした

>>大きくは、冒頭の3行を直すだけで、「実際にいれる」ことができ

 まさか、本当に3行を直した「だけ」で実行したのですか。

 #3 のソースをそのまま実行してみて下さい。
 データ配列が異なるので、思いどおりに出力しないのは当たり前ですが、「動く」はずです。
 (再度動作確認済み)

>おそらく勘違いされていると思うのですが、
>NとMの値はcsvの行・列数にしたいので、
>defineで決めるのがそもそもおかしい気がするのですが

 「NとMの値はcsvの行・列数」のあたりは、いじってませんし、
 私が「defineで決める」ようにしたわけでもありません。
 勘違いのしようがないのですが・・。

もし、勘違いしてたら、#3 で

>>(確認済み)

とはできませんよ。ダミーデータすらできないのだから。また、

>>なお、デバック段階では、N と M は下のように小さくし、ダミーデータもそれに揃えた方が楽です。

「NとMの値はcsvの行・列数」と認識しているからの記述です。
    • good
    • 0
この回答へのお礼

ありがとうございました

お礼日時:2011/03/24 07:12

No.2です。



>ほとんど理解できていません
正直、何を目指しているのか理解できません。
もし、宿題などであれば、理解もしないで答えだけ提出してもすぐにバレてしまいますし、今後を考えれば難しくなればなるほど、自分では解決できなくなってしまいます。
差し出がましいようですが、実行環境があるようなので、1ステップずつ実行してみたり、あるいはprintf()などで値を確認するなどして、理解された方がいいでしょう。

No.3さんより
>メモリ確保関数を用いるより、変数の宣言のほうが楽です。
この言葉どおりで、まずは大きめに領域を確保しておき、それに代入するのがいいでしょう。
たとえば、N行=1000,M列=100ぐらいで。
また、静的(static double csvdata[N][M];)に領域を確保した方が安心安全です。

ソースを見ると、確かにポインターを使って難しそうに見えます。
しかし、やっていることは、1行読み込んでそれからカンマ区切りを見つけながら、浮動小数点文字列をdouble値に変換し、2次元配列に代入しているだけです。
    • good
    • 0

#3 です。



>今は環境がないので実行できませんが、

★うぅ~、無銭飲食か~。

>これだと3行2列の形式にしか対応してないんじゃないですか?

>>なお、デバック段階では、N と M は下のように小さくし、

>これはコピペした時に空白が消えてしまっただけで、

★ならば正常に動きます。

>これをNとMがどんな値であれ読み込めるようにするにはどうすれいいでしょうか

>>N と M に変更があれば、コンパイルし直さなければなりません。

この回答への補足

実行してみましたが、動きませんでした

おそらく勘違いされていると思うのですが、
NとMの値はcsvの行・列数にしたいので、
defineで決めるのがそもそもおかしい気がするのですが

補足日時:2011/01/25 05:41
    • good
    • 0

>・・既存のプログラムは少しいじっただけなのでほとんど理解できていません


>実際にいれる文が欲しいです

 コンパイルしてみれば、少しは理解できると思います。
 大きくは、冒頭の3行を直すだけで、「実際にいれる」ことができました(確認済み)。

★要は、#define N23 と、#define N 23 は大違い、ということです。
+++++++++++++++ 以降、蛇足 ++++++++++++++++++++++
>型っていうのはMとNの値です

 N と M に変更があれば、コンパイルし直さなければなりません。
 このようなプログラムでは、(コンパイルし直す必要のない動的な)メモリ確保関数を用いるより、変数の宣言(◆)のほうが楽です。

 http://homepage3.nifty.com/mmgames/c_guide/q_mal …

 なお、デバック段階では、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
これはコピペした時に空白が消えてしまっただけで、実際には右側のように書いてあります

補足日時:2011/01/24 18:54
    • good
    • 0

ここまでできているのなら最初に1行読み込んで、列数を求め、以降はrealloc()で1行読むたびにメモリを拡張していけばいいでしょう。



でも
>型によらないCSVファイル…
この「型」って?

この回答への補足

型っていうのはMとNの値です
適切な言葉がわかりませんでした

プログラムを作成した、
とは言っても既存のプログラムは少しいじっただけなので
ほとんど理解できていません
実際にいれる文が欲しいです

補足日時:2011/01/23 22:16
    • good
    • 0

方法A


(1) とりあえず、一行読み込んで列数をカウント
(2) 一旦、行数カウントのためだけにファイルを読み込んで行数をカウント
(3) 必要なメモリを確保
(4) 今までと同じ流れでファイルの先頭から読み込み

方法B
とりあえず、一行読み込んで列数をカウント
一行分のデータを

次の行へのポインタ+配列

の線形リストにして、一行一行、
メモリ確保 → ファイルから一行読み込み → 確保したメモリにデータをセット
とする。
二次元配列でなければならないのであれば、一旦、線形リストの形で読み込んでおいて、
全行読み込んだ後でリストの先頭からたどって二次元配列にセットしなおすとか。

線形リストについては、
http://www.cc.kyoto-su.ac.jp/~yamada/ap/list.html
とか、検索するといろいろ出てくると思います。

この回答への補足

プログラムを作成した、
とは言っても既存のプログラムは少しいじっただけなので
ほとんど理解できていません
実際にいれる文が欲しいです

補足日時:2011/01/23 22:26
    • good
    • 0

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