• 締切済み

C言語を使って、ファイルの読み込みをして切り出して2次元配列に格納した

C言語を使って、ファイルの読み込みをして切り出して2次元配列に格納したいのです。 1,2行目に配列の行の数と列の数が書かれ、3行目から改行とカンマ、スペースで区切られて配列が書かれているテキストを読み込んで2次元配列に格納する。 テキストの例) 4 3 1.1 1.2 1.3 1.4 1.5 2.1 2.2 2.3 2.4 2.5 3.1 3.2 3.3 4.4 3.5 というプログラムを書いています。色々と参考書やサイトを参考してとりあえずの形にはなったと思ったのですが、実行してもエラーが出ます。 どこまで動いているか調べたところ、一行ごとに読み出してそれを切り出して行くところでおかしな事をしてしまっているようですが、どう変えたらいいものか分かりません。 なので、その点のアドバイスと 大きさの分からないファイルから1,2行目を読み出すのはこれで変な動きをする恐れはないか の2点についてヒントでも構わないので、教えてください。 以下、書いたソースです(申し訳ないのですが、文字数の関係で一部省略しています。) #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[] ) { double ** mainhairetu; int size_x, size_y; /* size_x 行 size_y 列 */ int i,j,count=0,count2; int *cut,*temp2; double temp; char s2[] = " ,"; char gyou[10],*num; FILE *fil; while((fgets(gyou,10,fil)) !=NULL){ if(count == 0){ size_x=atoi(gyou); count++; }else if(count ==1){ size_y=atoi(gyou); count=count+1; }else{ break; } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ここでmallocを使ってcutとmainhairetuの2つの配列を作っています。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ count=0; for (i = 0; i < size_y+2; i++) { mainhairetu[i][0] = atof( strtok( fgets(cut,50,fil),s2 ) ); for (j = 1; j < size_x; j++){ if(count <=1){ count++; break; }else{ mainhairetu[i][j] = atof( strtok( NULL,s2 ) ); } } } for(i=0;i<size_y;i++){ for(j=0;j<size_y;j++){ printf("%f",mainhairetu[i][j]); } printf("\n"); } return(0); }

みんなの回答

回答No.4

テキストの例) 5 3 1.1 1.2 1.3 1.4 1.5 2.1 2.2 2.3 2.4 2.5 3.1 3.2 3.3 4.4 3.5 を仮に dataファイルとすればもっと易しく考えて、それはスペース(空白文字)で区切られていることから fscanf() を使ったソースでも対応可能ですよ。 なお、動的二次元配列については↓を参照のこと。 http://www.aso.ecei.tohoku.ac.jp/~shun/multi_dim.html #include <stdio.h> #include <stdlib.h> #define MY_FILE "data" int main(void) { FILE *fp; int gyo,retu; int i,j; float **matrix; fp = fopen(MY_FILE, "r"); fscanf(fp, "%d", &retu); fscanf(fp, "%d", &gyo); matrix = (float **)malloc(sizeof(float *) * retu); if (matrix==NULL) exit(1); for (i=0; i<retu; i++) { matrix[i] = (float *)malloc(sizeof(float) * gyo); if (matrix[i]==NULL) exit(1); } printf("%s[%d][%d]:\n", MY_FILE, retu, gyo); for (i=0; i<gyo; i++) { for (j=0; j<retu; j++) { fscanf(fp, "%f", &matrix[i][j]); printf("%.2f ", matrix[i][j]); } printf("\n"); } fclose(fp); for (i=0; i<retu; i++) free(matrix[i]); free(matrix); return 0; } ----- 実行結果 ----- data[5][3]: 1.10 1.20 1.30 1.40 1.50 2.10 2.20 2.30 2.40 2.50 3.10 3.20 3.30 4.40 3.50

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.3

こっちにも問題ありますね。 > cut = malloc(sizeof(int)*size_y); よくみたら cutはint *ですね。fgetsの対象としておかしくないですか? Cの場合、ポインタの型が違っても警告が出るだけでコンパイルは通ったりしますが、そういう警告はできるだけ無視せず、目的の型と一致させるようにしましょう。 sizeof(int)が不明(環境依存のため)ですが、32bitCPU用だとすると4です。size_yは今回の場合3です。 4*3=12文字分しか確保していないのに、 > fgets(cut,50,fil) と50文字まで読もうしています。 今回の例では、1行(改行まで含めて)20文字あります。確保した12文字を越えています。 この時点で何が起っても不思議ではありません。 1行読む(=X方向にデータを読む)ためのバッファなのですから、基準にするのはsize_xの方でしょう。 > mainhairetu = (double**)malloc(sizeof(double*)*size_x); > for (i=0 ; i < size_x ; i++) { > mainhairetu[i] = (double*)malloc(sizeof(double)*size_y); これだと 2次元配列としては mainhairetu[size_x][size_y] という大きさになります。 しかし、実際に使用しているところを見ると mainhairetu[Y][X]として使用しています。 余る分には(Y側)ただ使わないだけですが、足りない分(X側)では領域をはみ出します。これも何が起っても不思議でない状態です。 どちらかに統一しましょう。一般的には[Y][X]を使います。 > size_yはテキストの行列の列分しかないので、テキストの最初にある行と列の2つ分のつもりです。 一応付け足すと、ファイルは基本的に一方通行です。あるところまで読んだら、次はその続きから読み込みはじめます。 今回は先で2行読み込んでいるので、このループは3行目から読み始めます。 fseek等で読み出し位置を変更できますが、読み込み先によっては変更できないものもあります(キーボードからの入力とか)

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

> テキストの例) > 4 > 3 この数字の意味はなんでしょう? 4列x3行だと、その後のデータと合わないのですが。 >while((fgets(gyou,10,fil)) !=NULL){ >if(count == 0){ .... これだと、「3行目」を読みに行きます。 1回目:conut=0→fgets1行目→whileの判定→if(count==0)→count ++; 2回目:→fgets2行目→whileの判定→if(count==1)→count=count+1; 3回目:→fgets3行目→whileの判定→else→break; 2回目でbreakさせるとか、 whileを((count <= 1)&&(fgets... としてcountで先に判定させるとかの工夫が必要です。 もっとも、これくらい回数なら、ループ使わずに2つ並べて書いた方がわかりやすくはないですか? 間違いというわけではないですが。 ・エラー対策(1行に10文字(9文字+改行)以上ある、数字ではない、など)が無い ・count++とcount=count+1が混在している > ここでmallocを使ってcutとmainhairetuの2つの配列を作っています。 これでは、この箇所に間違いは無いかの判断はできかねます。 > for (i = 0; i < size_y+2; i++) { この+2はなんでしょう? > mainhairetu[i][0] = atof( strtok( fgets(cut,50,fil),s2 ) ); ・これでは、fgetsやstrtokでエラーになっても対処できません。 ・この50はどこから来た値ですか?cutの配列長だったら、固定長で50程度なら、ややこしいmallocを使わずとも、最初からcut[50]で宣言しても十分です。size_x等から計算しているのなら、ここでもその値を使うべきです。 ・先の「while((fgets(gyou,10,fil)) !=NULL)..」の不具合のため、最初のデータ行(=ファイルの3行目)は9文字分読み込み済みで、ここのfgetsでは10文字目からの読み出しになります。 > if(count <=1){ > count++; > break; これの意図がわかりません。 これだと、 1番目と2番のデータ列では、それぞれmainhairetu[0][0],mainhairetu[1][0]にだけデータが入ってそれ以外は何も入りませんが。 > for(j=0;j<size_y;j++){ > printf("%f",mainhairetu[i][j]); > } size_yはsize_xの間違いだと思います。 書式に空白や幅の指定が無いため、全ての数値が連続して出力される(しかも値の大きさによって、長さがばらばらになる)ので、見栄えはよくないです。 > return(0); これくらいの長さで、すぐ終了するものならよいのですが。 mallocしたものは、使い終わったらfreeする習慣を付けましょう。

spagg
質問者

補足

テキストの例は 5 3 で5行*3列の間違いです。申し訳ありません。 3行目を読みに行ってしまうのは気が付きませんでした。ご指摘の様に修正してみます。 エラー対策は曲がりなりにも動いてから書く予定でした。 書き方の混在は修正しておきます。 文字数オーバーだったので仕方なく削ってしまいました。 その部分は cut = malloc(sizeof(int)*size_y); mainhairetu = (double**)malloc(sizeof(double*)*size_x); if (mainhairetu==NULL){ exit(1); } for (i=0 ; i < size_x ; i++) { mainhairetu[i] = (double*)malloc(sizeof(double)*size_y); if (mainhairetu[i]==NULL){ exit(1); } } となっています。 >> for (i = 0; i < size_y+2; i++) { > >この+2はなんでしょう? size_yはテキストの行列の列分しかないので、テキストの最初にある行と列の2つ分のつもりです。 ・エラーに関しては上と同様に動いてからと考えていました。 ・とりあえず大きめの数と思って50にしてしましましたが、一行で50ではマズイですね。 計算で出す方法を考えてみます。 ・上のfgetsの不具合を訂正すれば、大丈夫ですね。 2度目のfgetsを動かした時の動きがあやふやで テキストの最初の2行を飛ばして3行目から読み出すためのつもりでしたが、 でした。 不要だと分かったので、削除しておきます。 配列の出力は見やすい様に修正しておきます。 freeも一度ソースを貼りつけたときに溢れてしまって削ってしまいました。 多くのご指摘ありがとうございます。もう一度ご指摘の所を含めてソースを書き直してみます。

  • ave49952
  • ベストアンサー率18% (62/329)
回答No.1

考え方として、 1区画に格納する最大文字数を調べます。 上詰めか、下詰めかを決めます。 配列の数 を決めます。 バイナリーエディターでCRコードを読み出します。 1文字づつ読んで、カンマ又はCRコードが見つかるたびにメモリーに書き込みます。 CRコードの場合は、配列の行を変えます。 END OF FILEを検出したら、Print文を実行し、結果を確認します。 メモリーに書き込む時は、下詰めなら下から書き込んで、次の文字が見つかれば1文字上にシフトして書き込む手もあります。 結果は、ファイル出力をお忘れなく。 CSVファイルにするのなら、1文字づつ読み込んで、EOFを検出したら最後にCRを追加してファイル出力すれば終わりです。メモリーはCRの数をお忘れなく。

関連するQ&A

  • 【C言語を教えてください!】2次元配列について

    【C言語を教えてください!】2次元配列について 2次元配列を用いて、コンソール画面(ターミナル)の中央付近に*を表示させるプログラムを作りたいのですがさっぱり分かりません。 画面の大きさは半角で横80 縦25です。 25は奇数ですので40×12に表示したいと思います。 2重ループでなら出来たのですが配列でのやり方がわかりません。 #include<stdio.h> main() { int i,j; for(i=0;i<25;i++){ for(j=0;j<80;j++){ if(i==12&j==39){ putchar('*'); }else{ putchar(' '); } } } return 0; } 回答お願いします!

  • C言語の2次配列

    4行5列の配列を用意し、その配列の要素をtij(i=1~4, j=1~5)とするとき、tij=i×j となるように配列に値を入れ、 それを表の形で画面に表示するプログラムを作りなさい。  という問題なんですが、自分は下の様に組みましたがうまく動きません。どこが悪いのか教えてください。よろしくお願いします。あまりC言語を理解していないので申し訳ないですが、なるべく簡単に教えて頂ければ幸いです。 #include<stdio.h> int main(void) { int i,j; int t[4][5]; for(i=0;i<=3;i++) { for(j=0;j<=4;j++) t=i*j; printf("%5d",t); printf("\n"); } return 0; }

  • C言語 2次元配列で

    #include<stdio.h> int main() { char name[4][8]={"2013","mic","matsu","test"}; int i; for(i=0;i<4;i++) { printf("%s\n",name[i]); } return; } をいじって、配列の宣言をせずに、別の動作で同じ実行結果を得るためにはどうすればよいでしょうか? ただし、2次元配列を使用しないといけないのですが・・・。 すみませんが、できれば至急、ご指導、ソースコードの模範解答のほう、よろしくお願いします。

  • Cで2次元配列にCSVファイルを格納するには?

    ~.csvというファイルがあって、ファイルの中身は256*256のある数字の2次元配列です。 このファイルを読み込んで、2次元配列に格納したいのですが、どのようなソースを書けばいいのでしょうか? プログラミングをちゃんと勉強してこなかったために苦戦しています。 #include <stdio.h> #include <stdlib.h> int main(int argc,char *argv[]){ FILE *fi; char I[256]; char J[256]; if((fi=fopen("ch.csv","r"))==NULL){//csvもこのような記述でよみこむことができるのか? printf("ファイルオープンエラー\n"); exit(1); } while(fgets(J,256,fi) != NULL) { これより先が全く書けていない状態です。 ここまでもこのソースで大丈夫でしょうか? かなり基本的なことだと思いますが、よろしくお願いします。

  • 2次元配列とポインタの引数受け渡しについて

    2次元配列を関数に渡すときは、引数に渡す2次元配列と同じサイズを指定、もしくは2次元目のサイズのみ合わせて渡す方法がありますが、両方とも違うサイズで同じ関数を使いたいです。 最初は中身が同じで引数で受け取る2次元配列のサイズだけ、それぞれに合わせた引数を持つ関数を2つ作っていたのですが、なんだか冗長な気がしました。 そこで、2次元配列の先頭ポインタとサイズを受け取るようにすればいいのかと思い、テストとして次のプログラムを作成してみました。 #include <stdio.h> void func(unsigned char *a, int y, int x); int main(void) { unsigned char a[10][10]; func(a, 10, 10); printf("%d\n", a[7][4]); return 0; } void func(unsigned char *a, int y, int x) { int i, j; for (i = 0; i < y; i++) { for (j = 0; j < x; j++) { *(a + i*y + j) = i * j; } } } もちろんこれでも動くのですが、やはりこういう書き方はルールにはないので、コンパイルで警告が出ます。 a.c: In function ‘main’: a.c:10: warning: passing argument 1 of ‘func’ from incompatible pointer type a.c:4: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[10]’ このような書き方はやはりやめたいいのでしょうか。 また、その際はサイズ別に関数を作るしかないのでしょうか。 他にいい方法があれば教えていただけると助かります。

  • c言語のmalloc関数と2次元配列について

    ・mallocとreallocのAPPを作成しています、下記は単純化しました。 「質問-1」 ・while(1){...以下を無効にした場合、正常に終了します。 ・有効にして、最初に999を入力した場合、エラー表示されます。 ・この理由が分かりません。 「質問-2」 ・有効にして、初期数値(例えば11)を入力の場合、正常表示されます ・続けて数値(例えば15)を入力した場合、エラー表示されます。 ・この理由が分かりません。 ***************************************************************  #include <stdio.h>  #include <stdlib.h>  void MylnOut(void);  int **map;  int X=10,Y=10,i,j; //************************************************************** // MAIN //************************************************************** int main() {  char str[64]={""};  char *s="変更数値を入力(999で終了).... "; /* 2次元配列確保と初期表示 */  map=(int **)malloc(sizeof(int *)*X);  for(i=0;i<X;i++)   map[i]=(int *)malloc(sizeof(int)*Y);  MylnOut(); /* 変更数値入力 */ // while(1){ //  printf(s); //  gets(str); //  X=atoi(str); //  if(X==999) break; /* 領域変更と表示 */ //  map=(int **)realloc(map,sizeof(int *)*X); //  for(i=0;i<X;i++) //   map[i]=(int *)realloc(map[i],sizeof(Y)); //  MylnOut(); // } /* 領域開放 */   for(i=0;i<X;i++) free(map[i]);   free(map);   return 0; } //************************************************************** // 入力・表示 //************************************************************** void MylnOut(void) {  for(j=0;j<Y;j++)   for(i=0;i<X;i++) map[i][j]=55;   for(j=0;j<Y;j++){    for(i=0;i<X;i++) printf("%3d",map[i][j]);    printf("\n");   } }

  • C言語で半角スペースをデリミタとしたデータファイルを読み込むプログラム

    C言語で半角スペースをデリミタとしたデータファイルを読み込むプログラムを作っています. まず, データのレコード数とフィールドの数をカウントしてその後, double型の2次元配列に必要なメモリ領域をmalloc関数にて確保して, 2次元配列にデータを代入していくという処理をやらせています. とうプログラムをコンパイルして実行した所以下のようなエラーが出てどうやらFreeを2重にしてしまっていることがエラーログから分かるのですが, どこを修正していいかわかりません. どなたかお力を貸していただけないでしょうか? ■ソース■ #include<stdio.h> #include<string.h> #include<malloc.h> #define LINE_MAX_SIZE 5120 int main(void){ __road("test.dat"); } int road(char fname[100]){ __FILE *fp; __char line[LINE_MAX_SIZE]; __char *tp; __char *token=" "; __int rn=0; __int fn; __int i=0; __int j=0; __ __fp=fopen(fname,"r"); ____/* ____ check record number and field number ____*/ ____while(fgets(line,sizeof(line),fp)){ ______ ______tp=strtok(line,token); ______fn=0; ______while(tp!=NULL){ ________tp=strtok(NULL,token); ________fn++; ______} ______rn++; ____} ____//printf("rn=%d fn=%d\n",rn,fn); ____/* ______make 2 dimension array dynamically ____*/ ______double **data; ______ ______data=malloc(sizeof(double *)*rn); ______// then data have 2 pointer to double type ______for(j=0;j<fn;j++){ ________data[j]=malloc(sizeof(double)*fn); ______} ____ ____/* ____ insert data to array ____*/ ____i=j=0; ____while(fgets(line,sizeof(line),fp)){ ______j=0; ______tp=strtok(line,token); ______data[i][j]=atof(tp); ______while(tp!=NULL){ ________j++; ________tp=strtok(NULL,token); ________data[i][j]=atof(tp); ______} ______i++; ____} ____for(i=0;i<rn;i++){ ______for(j=0;j<fn;j++){ ________printf("%f ",data[i][j]); ______} ______printf("\n"); ____} ____/* ____/ free memory for data[][] ____*/ ____if(data){ ______for(j=0;j<fn;j++){ ________if(data[j]){ __________free(data[j]); ________} ______} ______free(data); ____} __fclose(fp); } /* int freeMem(){ } */

  • 2次元配列

    課題で、氏名をローマ字で入力し、2次元配列に格納するプログラムを作成するというのがでました。条件として、氏名の長さは10文字以下、最大件数は10件。1エントリ入力ごとに累計件数を表示し、10件目の入力が完了するか、氏名の一文字目に'0'が入力されたら入力を終了しデータを表示する。11文字以上入力されたら、先頭から10文字までを有効とし、11文字目以降を無視する。 改行のみの入力の場合、エラーメッセージを表示し、再入力させる。 初心者の私には、データの表示と、条件の処理の仕方がわかりません。 下記プログラムを上記の条件を満たすようにするには、どこを直したらよいか教えてください。 お願いします。 #include <stdio.h> #include <string.h> #define BUFFERSIZE 1024 main() { char str[10][BUFFERSIZE]; char c; int count = 0; int i; int j; int l[10]; /*氏名の入力*/ for (i = 0; i < 10; i++) { printf("氏名人力 : "); while ( (c = getchar()) != '\n' ) { if( count < BUFFERSIZE - 1 ){ str[i][count++] = c; } } str[i][count] = '\0'; printf("累計 : %d \n", i+1); } for (i = 0; i < 10; i++) { for (j = 0; j < 10; j++) { printf("%c",str[i][j]); } putchar('\n'); } return 0; }

  • 2次元配列を使ったC言語の九九表を作りたいんですが、方法がわかりません。

    C言語で、九九の表を作っているのですが2次配列を使わないでの方法なら出来るんですが、 2次配列を使うと出来なくなってします。 下記のように途中まで組んだのですが、どうしてもエラーがでてしまいます。 #include <stdio.h> int main(void) { int i,j,a[9][9]; printf(" "); for(i=1; i<=9; i++) printf("%3d", i); printf("\n"); for(i=0; i<9; i++){ for(j=0; j<9; j++) a[i][j]= {1,2,3,4,5,6,7,8,9},{2,4,6,8,10,12,14,16,18},{3,6,9,12,15,18,21,24,27},{4,8,12,16,20,24,28,32,36},{5,10,15,20,25,30,35,40,45,6,12,18,24,30,36,42,48,54,7,14,21,28,35,42,49,56,63},{8,16,24,32,40,48,56,64,72},{9,18,27,36,45,54,63,72,81} }; for(i=0; i<9; i++){ printf("%3d", i+1); for(j=0; j<9; j++) printf("%3d",a[9][9]); printf("\n"); } return 0; } とやったのですが…以下に書く部分が間違っているようで。 #include <stdio.h> int main(void) { int i,j,a[9][9]; printf(" "); for(i=1; i<=9; i++) printf("%3d", i); printf("\n"); for(i=0; i<9; i++){ for(j=0; j<9; j++) a[i][j]=□ } for(i=0; i<9; i++){ printf("%3d", i+1); for(j=0; j<9; j++) printf("%3d", □); printf("\n"); } return 0; } 色々調べたり、少しずつ変えながら試しているのですが、できません。 どなたかわかるかたいらっしゃいますか。間違いがわかりません… 配列を使用しなくても出来ることは、わかるのですが、配列を使うバージョンでもできるようになりたいんです。 私がしようとおもっているのは、81個分の値を先に計算し、9×9の2次元配列に格納し、次に81個の配列要素の値を出力したいのですが、 間違いと方法がわかる方いらっしゃいませんか。

  • C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問

    C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問です! ファイルを開いた後でエラーとなるのですが、何が足りないのでしょうか? ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int k,i=0; int num[10]; char na[10][20]; fp=fopen(argv[1],"r"); if(fp==NULL){ printf("ファイルを開けません\n"); return 1; }else{ printf("開けた\n"); } while(fgets(str,sizeof str,fp)!=NULL){ tp=strtok(str," "); num[i]=atoi(tp); tp=strtok(NULL," "); strcpy(na[i],tp); i++; } printf("%d\n%s\n",num[0],na[0]); printf("%d\n%s\n",num[1],na[1]); fclose(fp); return 0; }

専門家に質問してみよう