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

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);
}

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



#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
    • good
    • 1

こっちにも問題ありますね。



> 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等で読み出し位置を変更できますが、読み込み先によっては変更できないものもあります(キーボードからの入力とか)
    • good
    • 0

> テキストの例)


> 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する習慣を付けましょう。

この回答への補足

テキストの例は

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も一度ソースを貼りつけたときに溢れてしまって削ってしまいました。


多くのご指摘ありがとうございます。もう一度ご指摘の所を含めてソースを書き直してみます。

補足日時:2010/11/09 00:49
    • good
    • 0

考え方として、


1区画に格納する最大文字数を調べます。
上詰めか、下詰めかを決めます。
配列の数 を決めます。
バイナリーエディターでCRコードを読み出します。
1文字づつ読んで、カンマ又はCRコードが見つかるたびにメモリーに書き込みます。
CRコードの場合は、配列の行を変えます。
END OF FILEを検出したら、Print文を実行し、結果を確認します。

メモリーに書き込む時は、下詰めなら下から書き込んで、次の文字が見つかれば1文字上にシフトして書き込む手もあります。

結果は、ファイル出力をお忘れなく。

CSVファイルにするのなら、1文字づつ読み込んで、EOFを検出したら最後にCRを追加してファイル出力すれば終わりです。メモリーはCRの数をお忘れなく。
    • good
    • 0

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