• ベストアンサー

ファイル読み書き方法について(r+)

現在ファイルへのc言語でファイル書き換えを行おうとしているのですが その部分だけ表すとこんな感じです。 ※多少省いていますので括弧の数等の間違えがあっても見逃してください。 if((fp = fopen("aaa.txt", "r+")) == NULL) ; else{      while((ch = fgetc(fp)) != EOF){        if(ch == '\n'){          name[i] = '\0'          i = 0;          continue;        }        name[i] = ch;        i++        if(strcmp(name, name_a){          fprintf(fp, "%s", coment);        }      } } ファイルを読み込んでいって、ある名称が見つかったらその後ろの コメントを更新するというプログラムなのですが。 fprintfまで着ていることは確認できたのですが、なぜか値が書き換わりません。 c言語ではファイル書き換えはあまり一般的ではないということは聞いていて、 以前はすべて読み込んで、一部分を変えてすべて書き込む方法をとって いましたが、効率が悪い気がしたのでこのやり方を試しています。 1 やり方、関数の使い方が間違っているならば正しい方法を教えてください。 2 そもそも"r+"の使い方がおかしい場合は元のやり方でやります。ただし   "r+"の使い方が詳しく乗っているサイトが見つからなかったので、   わかりやすいサイト等ありましたら、教えてください。 3 ファイル読み書きで検索するとf_seekなる言葉をよく見かけるのですが、   あれを使わないとだめなんでしょうか? 1つでもいいので教えてください。

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

  • ベストアンサー
  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.1

# 検証していないので ...      while((ch = fgetc(fp)) != EOF){        if(ch == '\n'){          name[i] = '\0'          i = 0;          continue;        }        name[i] = ch;        i++        if(strcmp(name, name_a){          fprintf(fp, "%s", coment);        } を      while((ch = fgetc(fp)) != EOF){        if(ch == '\n'){          name[i] = '\0'          i = 0;          continue;        }        name[i] = ch;        i++        if(strcmp(name, name_a){          fseek( fp, 0, SEEK_CUR );          fprintf(fp, "%s", coment);          fseek( fp, 0, SEEK_CUR );        } といった具合にして fseekで挟んでみましょう もしくは ftelで現在位置を取得 ファイルの先頭へfseek 元位置にfseek 書き込み ftelで現在位置を取得 ファイルの先頭へfseek 元位置にfseek としたほうがいいのかも ... VC++のヘルプでは r+/w+での読み書きのモード変更時には fsetpo/fseek/rewindのいずれかを実行しなさい と明記されていました

napanapana
質問者

お礼

ご回答ありがとうございます。 このままの形ではできませんでしたが、 これを元に調べ、何とか作成できました。 やはりfseek等を使わないと無理みたいですね。

その他の回答 (2)

回答No.3

基本的な考え方が間違っています。 他の回答のようにfseekで挟んだとしても「ファイルに対し、挿入や削除は出来ない」ので、単にコメントをfprintfするだけだと、とても困った事になります。 例えば、以下のようなファイルで「123の後ろに、コメント『//456789abc』を書き足す」と、どうなるでしょうか? あいう 123 ABC 012 ### xyz 結果は あいう 123//456789abc.### xyz になります。(「.」の部分は文字化けが起こる) あいう 123//456789abc ABC 012 ### xyz にはなりません。 また、元のコメントが「//456789abc」だった時に、コメントを「//abc」に短くしたら、どうなるでしょうか? あいう 123//456789abc ABC 012 ### xyz は あいう 123//abc789abc ABC 012 ### xyz になります。 あいう 123//abc ABC 012 ### xyz にはなりません。 質問者さんの方法でうまく動くのは「書き換え前のコメントと、書き換えた後のコメントの『バイト数』が、同じ場合だけ」です。 上記のように「コメントのバイト数が変わる」と失敗します。 こういう場合は ・元のファイルを読み込みモードで開く。 ・別のファイルを新規作成モードで開く。 ・元のファイルから1行読み込む。 ・必要ならば、読み込んだ行をメモリ上で加工(コメントを足したり、コメントを短くしたり、コメントを長くしたり、コメントを削除したりする)する。加工が必要ないなら、そのままにする。 ・加工が終わった行を、新規作成したファイルに書き込む。 ・元のファイルがEOFになるまで「読んで、加工して、書き込んで」を繰り返す。 ・繰り返しが終わったら、両方のファイルを閉じる。 ・ファイルを閉じたら、元のファイルをunlinkなどで削除する。 ・新規作成して閉じたファイルをrenameなどで「元のファイルのファイル名にリネーム」する。 と言う処理をしましょう。

napanapana
質問者

お礼

ご回答ありがとうございます。 昔このやり方を使っていたのですが、 (学校の先生がこのやり方が一般的だといっていたため) 一部分だけを書き換える場合にファイル全てを読み込ま ない方法を探していたため、r+での実装を考えていました。 一応、No1さんの内容を参考に、サイトなどを確認しfseekで作成できました。 バイト数は合わせてあります。

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

+ の付いたモードで fopen した場合, 入出力を切り替えるときには基本的に位置を設定する関数 (fseek, fsetpos, rewind) が必要です. ただし, 入力から出力に切り替えるときには ・fflush でも OK. ・ファイルの最後まで読んでしまったときには関数を呼び出さなくてもいい. だそうです.... と, 規格にはちゃんと書いてある.

napanapana
質問者

お礼

ご回答ありがとうございます。 詳しい説明が書いてあるサイト等を 教えていただけると助かります。

関連するQ&A

専門家に質問してみよう