- ベストアンサー
ファイルの読み書き方法についての質問
- ファイルの読み書き方法について質問です。ファイルを最初に書き込まないといけない場合、最後に文字列を追加する方法を知りたいです。
- 現在ファイルに文字列を追加していますが、最後ではなく最初に文字列を追加したいと考えています。しかし、ファイルを2回ずつ読み込んだり書いたりするとバグが発生します。
- 追加したい文字列が「もも」で、現在のデータが「オレンジ」と「みかん」の場合、「もも」「オレンジ」「みかん」の順でデータが追加されることを目指しています。しかし、バグのために「みかん」が3列書き込まれてしまいます。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
原因はbufを使いまわしてしまっているからです。 tmはポインタの配列なので、bufの中身ではなくbufの場所を格納しているだけですからtm[0]~[2]は全部同じ場所を格納してしまっています。 tmにbufを格納する時は以下のように別の領域を確保、コピーして格納するようにします。 tm[i] = (char*)malloc(strlen(buf)+1); strcpy(tm[i], buf); 最後にmallocで確保した領域をfreeするのも忘れずに
その他の回答 (1)
- hanabutako
- ベストアンサー率54% (492/895)
壊れる根本の原因は#1さんが指摘したとおりですが、それ以外にも色々と突っ込みたいところがあるのでちょっと書きます。 1. fopenでちゃんとファイルを開けたかチェックしてない。 2. tmのサイズは1000しかないのに、1000件以上fgetsで読むようになっている。 3. そもそもマジックナンバーを即値で書いて使いまわしているのが最悪。例えば、tmのサイズを1000件から10,000件に変える場合の変更点は無数になる。 4. moto.txtからchat_deta.txtに変換したら改行が増えている。 5. chat_detaのスペルがおかしい。 6. 何行読み込んだかを管理している変数が特にない。 7. 読み込みのコードが同じようなことをする割に2回出てくる。 ...というところを考慮してちょっと書き換えてみました。 #include <err.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_TMS 1000 #define BUF_SIZE 400 /* * Reutnrs number of read entries if success. Otherwise, -1. * Notices: * - If max_entries are less than number of lines in the file, this only read * max_entries lines. * - This skips empty line. * - This automatically allocate memory for each entries. You SHOULD free them by yourself. */ static int read_entries(const char *filename, char** entries, size_t max_entries) { char buf[BUF_SIZE]; size_t num_entries = 0; FILE *fp; fp = fopen(filename, "r"); if (fp == NULL) return -1; while (fgets(buf, sizeof(buf), fp) != NULL && num_entries < max_entries) { /* remove end of line if exist. */ int last_char_idx = strlen(buf) - 1; if (last_char_idx >= 0 && buf[last_char_idx] == '\n') { buf[last_char_idx] = '\0'; last_char_idx--; } /* skip emtpy line */ if (last_char_idx < 0) continue; entries[num_entries] = strdup(buf); if (entries[num_entries] == NULL) { /* if failed to allocate memory, clear all entries and returns -1. */ size_t i; for (i = 0; i < num_entries - 1; i++) { free(entries[i]); } fclose(fp); return -1; } printf("entry[%ld]: %s\n", num_entries, entries[num_entries]); num_entries++; } fclose(fp); return num_entries; } int main(void){ FILE *fp; char *tm[MAX_TMS]; size_t filled = 0; int i, num_read; fp= fopen("now.txt","w+"); fprintf(fp,"もも"); fclose(fp); //何で最初に書き込んでるんだ? //という突っ込みがあるでしょうが本当に作りたいプログラムは最初にファイルに書き込まないといけないためです。 num_read = read_entries("now.txt", &tm[filled], MAX_TMS - filled); if (num_read < 0) err(errno, "failed to read entries"); filled += num_read; //printf("%s<br>",tm[0]);//確認用 この時点ではtm[0]に"もも"が入っている num_read = read_entries("moto.txt", &tm[filled], MAX_TMS - filled); if (num_read < 0) err(errno, "failed to read entries"); filled += num_read; for (i = 0; i < filled; i++) { printf("%s<br>",tm[i]); } fp =fopen("chat_data.txt","w+"); for(i=0;i < filled; i++){ fprintf(fp,"%s\n",tm[i]);//バグった内容が書き込まれるためみかんが3列かきこまれる } fclose(fp); for (i = 0; i < filled; i++) { free(tm[i]); } return EXIT_SUCCESS; } #1さんは最初からmallocで容量を確保する方法をおすすめしていますが、どうせmallocするなら必要になってから確保するほうが自分は好きです。メモリーを新たに割り当てて、コピーをするのはstrdupで一発でできるので簡単です。ただ、このプログラムだと最初から2次元配列を作っておけばmallocをしなくてもよいと思います。 あと、ぱっと見これはチャットサイトを作るためのCGIに見えるので追加アドバイス。 8. ファイルから読んで、書くまでロックをかけて守ったほうがいいです。競合状態で書き込みが起きるとデータが消えます。 9. 文字列処理をするのにC言語はあまり向いていません。個人的にはCGIはC言語で書くよりPerl、Ruby、Python、PHPなどのスクリプト言語で書くほうがおすすめです。(達成したい方向と違うところで苦労するので) 10. それでもC言語でやりたいなら、string.hに入っている関数にひと通り目を通しておくことをおすすめします。あとは場合によってはregexライブラリーのお世話になったほうがいいこともあります。 まぁ、頑張って。
補足
<<壊れる根本の原因は#1さんが指摘したとおりですが、それ以外にも色々と突っ込みたいところがあるのでちょっと書きます。 何やらそんなに突っ込まれるところがあるのですね 見た感じちゃんとできてるのでよく分からなかったです 2. tmのサイズは1000しかないのに、1000件以上fgetsで読むようになっている。 ここら辺はちゃんと修正しないといけませんね 指摘ありがとうございました 3. そもそもマジックナンバーを即値で書いて使いまわしているのが最悪。例えば、tmのサイズを1000件から10,000件に変える場合の変更点は無数になる。 すみません言ってる事が全く分かりません もう少し区分かりやすく解説お願いします 変なバグをのこしたくないので 4. moto.txtからchat_deta.txtに変換したら改行が増えている。 まさしくこれが取り除きたいバグとして奮闘してました <<あと、ぱっと見これはチャットサイトを作るためのCGIに見えるので追加アドバイス。 よく分かりましたね すばらしいです 8. ファイルから読んで、書くまでロックをかけて守ったほうがいいです。競合状態で書き込みが起きるとデータが消えます。 これについては後々処理するつもりでいました しかし今の問題を解決するのがが先ですね 9. 文字列処理をするのにC言語はあまり向いていません。個人的にはCGIはC言語で書くよりPerl、Ruby、Python、PHPなどのスクリプト言語で書くほうがおすすめです。(達成したい方向と違うところで苦労するので) 10. それでもC言語でやりたいなら、string.hに入っている関数にひと通り目を通しておくことをおすすめします。あとは場合によってはregexライブラリーのお世話になったほうがいいこともあります。 いつも言われますよPealにしろと しかし私にもCでやらないといけない理由があるためアドバイスどおりstring.hに入っている関数にひと通り目を通しておきます。 後指摘もソースを書いていただいた事も本当にうれしいです うれしいのですが、これは私では使用できません なぜなら #include <err.h> #include <errno.h> とかの関数郡を全く知らないのです。 いつかは私も使うようにはなるかもしれませんが、まだ手一杯でして(mallocもつい最近知るようになったのですがこの関数自体がまだよく分からず使っているところがある)余裕がないのです そのため私から見てソース自体が意味が分からないところがいくつかあります。 本当にソースを書いていただきありがとうございました もしよかったらC言語の先生になっていただきたいのですが無理は言えませんね 本当にありがとうございました
補足
ありがとうございます 読み込む事ができるようになりました ただかきこみ字に新たなバグ画出てきたのでそのバグを取り除くための新たなバグに奮闘してました