• ベストアンサー

ポインタ(追加質問)

http://okwave.jp/qa5092628.html の続きです。補足にいれようかとも思いましたが 以前より前の質問は占めたほうがいいと言われつづけてたので 閉めてしまいました。 #include <ctype.h> #include <string.h> #include <stdlib.h> #include <stdio.h> typedef struct{ int number; char *class_type; char *name; char *subject; } my; my *data; int main(int argc, char* argv[]) { FILE *fp; int field = 0, line = 0; char buf[1000], *str; char bufG[1111]; int i, non_value = 0; data = malloc(sizeof(my)*100); //最大100人分とる。それを越えたケースは今は考慮しない。ここを変更 if((fp=fopen("test.txt","r"))==NULL){ printf("ファイルが開けません"); } while(fgets(buf,1000,fp) !=NULL){ str=buf; while(*str != '\0'){ if(*str != ','){ for(i = 0; *str != ',' && *str != '\0' ; i++){ if(*str == '\n'){ } else{ bufG[i] = *str; } str++; } bufG[i] = '\0'; switch(field){ case 0: data[line].number=atoi(bufG); //ここを変更 break; case 1: data[line].class_type = malloc(strlen(bufG)+1); //これを追加 strcpy(data[line].class_type, bufG); break; case 2: data[line].name = malloc(strlen(bufG)+1); //これを追加 strcpy(data[line].name, bufG); break; case 3: data[line].subject = malloc(strlen(bufG)+1); //これを追加 strcpy(data[line].subject, bufG); break; } field++; } else{ str++; non_value++; if(non_value == 2){ switch(field){ case 0: data[line].number=' '; //ここを変更 break; case 1: data[line].class_type = malloc(3); //これを追加 strcpy(data[line].class_type, " "); break; case 2: data[line].name = malloc(3); //これを追加 strcpy(data[line].name, " "); break; case 3: data[line].subject = malloc(3); //これを追加 strcpy(data[line].subject, " "); break; } non_value = 0; str++; field++; } } line++; field = 0; } //ここはおまけ for (i =0; i < line;i++){ printf("%d:%s:%s:%s\n", data[i].number,data[i].class_type,data[i].name,data[i].subject); } fclose(fp); return 0; } 前回載せてもらった解答ではcsvファイルに空欄があると 値が代入されなかったのでその機能をつけました。 具体的には,が連続してあると(csvファイルに空欄がある場合1,A,,数学のように,と,の間には何もない)場合半角スペースを入れてます。 この場合例えば1,A,,数学のように名前の欄を空白にするとdata[0].name は半角スペースが入ってますが その次の値、すなわちdata[0].subjectの値がばぐった値になっています。 改善方法を教えて下さい。 もう1点あります。上のソースでは data = malloc(sizeof(my)*100); //最大100人分とる。それを越えたケースは今は考慮しない。ここを変更 とありますがこれを while(fgets(buf,1000,fp) != NULL){ str = buf; data = malloc(sizeof(my)*100); ←このへんに  if(line > 100)){ //メモリ追加処理 } のようにすることは可能ですか?

質問者が選んだベストアンサー

  • ベストアンサー
  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.3

以下のようにして下さい。 但し、,,のとき、半角スペース1バイトを入れる形でなく、,,の形式で表示できるようにしています。(文字列長=0にしてます) どうしても、半角スペース1バイトを入れたいなら、その旨補足して下さい。 ---------------------------------------- #include <ctype.h> #include <string.h> #include <stdlib.h> #include <stdio.h> typedef struct{ int number; char *class_type; char *name; char *subject; } my; my *data; int main(int argc, char* argv[]) { FILE *fp; int field = 0, line = 0; char buf[1000], *str; char bufG[1111]; int i; data = malloc(sizeof(my)*100); if((fp=fopen("test.txt","r"))==NULL){ printf("ファイルが開けません"); } while(fgets(buf,1000,fp) !=NULL){ str=buf; while(*str != '\0'){ //削除 if(*str != ','){ for(i = 0; *str != ',' && *str != '\0' ; i++){ if(*str == '\n'){ } else{ bufG[i] = *str; } str++; } bufG[i] = '\0'; //printf("%s",bufG); switch(field){ case 0: data[line].number=atoi(bufG); break; case 1: data[line].class_type = malloc(strlen(bufG)+1); strcpy(data[line].class_type, bufG); break; case 2: data[line].name = malloc(strlen(bufG)+1); strcpy(data[line].name, bufG); break; case 3: data[line].subject = malloc(strlen(bufG)+1); strcpy(data[line].subject, bufG); break; } field++; if (*str != '\0') str++; //これを追加 //削除 } //削除 else{ //削除 str++; //削除 } } line++; field = 0; } for (i =0; i < line;i++){ printf("%d:%s:%s:%s\n", data[i].number,data[i].class_type,data[i].name,data[i].subject); } fclose(fp); return 0; } ---------------------------------------- linux(gcc)で動作確認済みです。 2番目の質問は、100行を越えたとき、さらにその分も、追加したいということでしょうか。 その場合の対処方法は、2つのやり方があります。 1番目の方法: 最初に何行あるか、ファイルを読み込み、行数のみをカウントする。 行数カウント後、一旦ファイルをクローズする。 再度、ファイルをオープンする。取得した行数分でmallocする。 あとは、今までと同じ。 2番目の方法: 何行毎にメモリをとりなおすか、決めておく。(例えば100行とする) 最初に100行mallocしておく。 101行目になったとき、100行(今までの分)+100行(今回の追加)=200行分のメモリをmallocする。 今までの100行分のデータを新規の領域にコピーする。 コピー完了後、今までのデータのmallocしたメモリを解放する。 新規のメモリの101行のところへ今度の101行のデータをセットする。 以降は201,301行毎に上記と同じ処理を行う。 どちらの方法(1番目、2番目のどちらか)を望むか補足して下さい。 たぶん1番目は、質問者様のレベルなら自分でできると思います。 2番目の方法は、それなりに多少難しいです。

rooding
質問者

補足

1の方法が一番メモリを最少に抑えれる気がしますね。 こちらはすぐできました。 少し気になったのはcsvファイルで番号、クラス、名前、教科を それぞれ空欄にして見た場合に 1,番号が空欄→0と表示 2,クラスが空欄→空欄が表示 3,名前が空欄(例:1,A,(空欄),国語→data[0].nameには空欄が表示されるが次の国語が 国語フと表示されます。これはおそらく終端文字の位置が 1個ずれてるんだと思いますがどうでしょう? 4,教科が空欄→・と表示 こちらも3と同じなのかな。 お手数ですが確認してもらえますか?

その他の回答 (5)

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.6

バグってますね。以下のfor文で for(i = 0; *str != ',' && *str != '\0' ; i++){ if(*str == '\n'){ bufG[i] = '\0';//この行を追加して下さい。 } else{ bufG[i] = *str; } } テストデータは以下の通り -------------------------- 1,A,,音楽 2,B,本田, 3,B,松本,美術 ,,,ab ,,,ab ,,,cdc ,, , ,,, 6,,横野,音楽 7,A,,音楽 ---------------------- 実行結果は以下の通り ------------------------ 1:A::音楽 2:B:本田: 3:B:松本:美術 0:::ab 0:::ab 0:::cdc 0:: : 0::: 6::横野:音楽 7:A::音楽 ----------------------- これから、しばらく離れます。20時頃、戻ります。

参考URL:
・・
rooding
質問者

補足

いや長い間ほんとありがとうございました。 とりあえず問題なく動きますので後は理解に励みます。 この問題はとりあえず解決したのでしめておきます。 ありがとうございました。

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.5

#3です。 >1,番号が空欄→0と表示 そのつもりです。int型の変数にスペース1バイトをいれると、 0x20=32となり、32の数値をいれたのと同じ事になります。 従って、どうしてもスペースにしたいなら、表示のときに、 番号が0ならスペースを表示するようにするしかありません。 >2,クラスが空欄→空欄が表示 そのつもりです。スペース1バイトを表示したほうが良いですか? >3,名前が空欄(例:1,A,(空欄),国語→data[0].nameには空欄が表示さ>れるが次の国語が 国語フと表示されます。これはおそらく終端文字の位置が >1個ずれてるんだと思いますがどうでしょう? >4,教科が空欄→・と表示 >こちらも3と同じなのかな。 テストデータを提示して下さい。 そのデータでこちらで試験してみます。

rooding
質問者

お礼

番号とクラスですが 今後これらにアクセスして値が一致するもの行ごとに 例:クラスと好きな教科が同じものをソートする。 1,A,山田,国語 2,B,加藤,数学 3,B,山口,数学 4,A,冨士,国語 ↓ 1,A,山田,国語 4,A,冨士,国語 3,B,山口,数学 2,B,加藤,数学 のようなかんじに ソートするというのが本来の目的で、比較するときに問題なければ 大丈夫です。

rooding
質問者

補足

ネットが使えるPCの方はエクセルが入ってないので添付はできませんが 一行のcsvファイルで1 A (空欄) 国語と1 A 山田 (空欄)です つまり  A B C D  ・・・ ------------ 1 1 A (空欄) 国語 2 3 ・ ・ ・ と   A B C D  ・・・ ------------ 1 1 A 山田 (空欄) 2 ・ ・ ・ これでわかるかな。多分こんな感じになってると思います この2パターンですが少し面倒ですが ご確認お願いします。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

変数 non_value って何をしているんだろう. 本題とは関係ないし趣味の問題なのかもしれないのですが, 関数を作ってやるだけでかなり処理の見通しがよくなるはずです. 例えば fgets は最後に改行があったりなかったりしてうっとうしいので 「fgets して最後に改行があったらそれを取り除く」 関数を作ったり 「malloc して strcpy」 の strdup を作ったりすると main の処理が見やすくなります. あと, なんとなく「読んだ人を不安にさせる」雰囲気があるので, その辺を整理するとよりよいプログラムになると思います. 例えば, 現状では「field++」が 2か所にあるのですが, if の外に出せば 1か所ですみますし, そうすれば「while ループを 1回まわるたびに field の値が 1ずつ増える」ことが明確になります. また, ループの最後で field=0 を実行していますが, これは本来 while ループに入る直前にあるべきです. もしくはこの 2つをまとめて forループにしてしまうというのもあり.

回答No.2

>もう1点あります。上のソースでは (略) >のようにすることは可能ですか? 幾つか前の過去質問で当方が示したサンプルは、件数が増えると自動的にメモリを拡張し、何件でも読めるようになっているのですが。 当方の回答がよっぽど気に食わないのか、完全にそれをスルーされてますね。あの回答のあとも、当方のサンプルに書かれた色々な書き方や技法が微塵も反映されてないようですし。 あのサンプルを見れば「足りなくなる前に自動で増やせる」のが判る筈で、こんな質問が出て来る筈がありません。 こんな質問が出るって事は「難しくて何やってるか良くわかんないから、あの回答は見なかった事にしよう」っていう対応をしているとしか思えません。 貴方は、得られた回答を吸収して本気でプログラムを改善していこうって気持ちがありますか? 質問文や回答のお礼の行間から「都合の悪い事は、見なかった事にしよう」と言う態度が随所に垣間見えるのですが。

rooding
質問者

補足

誤解なさらず。貴方からいただいてサンプルをベースにし 格納方法(最近まで質問していた所です)をとりいれたプログラムは もう既にできあがっています。若干の問題はありますが。 この質問は貴方のサンプルを組み込まなく 自分で考えてくみ上げてるプログラムです。 正直貴方のサンプルは非常に丁寧なコメント入りですが 箇所によっては分からない所もあります。 それに対する質問をせずに保留、そして自分で考えたプログラムを 使って組んでみました。それが現在で 可能ですか?という質問はしましたが、そのままの意味ではなく 実際本文にあるソースコードをベースに貴方のやり方を参考にして 組んだらコンパイルエラーにはなりませんがエラーになったんですよ。 (大筋をとりいれただけで全く同じものではありませn) そこで何故?となって位置がおかしいのかとなって質問内容が 少し不自然なきもしますが質問しました。 要は理解は乏しいかもしれないが見なかった事にしようとしてるわけじゃありませんよ。 一番誤解されているのは >あの回答のあとも、当方のサンプルに書かれた色々な書き方や技法が微塵も反映されてないようですし ここです。 strtokの方法では私はうまくできませんでした。 しかしポインタをずらしていくという考え方のヒントを得て それなら以前使っていた構造体のメンバの固定長でのプログラムなら できているのでそちらはその方法でやっていた。若干やり方は異なりますが 考え方自体は同じはず。 それで最初に作っていた固定長でのプログラムをベースに それを可変長にする質問を繰り返していたんですよ。 要はその固定長のプログラムを可変長にする というのがメインで 他はとりあえず保留しておいた。それだけです。 後は貴方のサンプルのメモリ拡張方法を理解するだけ。 組みこむだけならもうできているが、断片的にしか理解できていない。 それで自分がつくっていたプログラムを可変長にするプログラムの サンプルをもらいそれをベースにしてメモリ拡張してみようとしたが 失敗。コンパイルエラーではないのでどこが悪いのかわからず 変な質問になってしまっていますが・・ 理解力は乏しいですが無視してるわけじゃありません。 以上です。

  • minaraiH
  • ベストアンサー率25% (1/4)
回答No.1

メモリだけど取得位置変では?

rooding
質問者

補足

すいません。みすってしまいまして while(fgets(buf,1000,fp) != NULL){ str = buf; if(line > 100)){←このへんに //メモリ追加処理(reallocなどでとりなおす) data = malloc(sizeof(my)*100); の位置は動かさずに 上記の感じになります。

関連するQ&A