• ベストアンサー

ファイル読み書き方法について(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)

  • chie65535
  • ベストアンサー率43% (8525/19379)
回答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

  • ファイルをオープンするときのエラー

    C言語であるファイルにある数値を100ごとに合計して,ほかのファイルに書き出す。しかし,実行するとエラーでてきます。原因はわからないです。因みに,オープンしたいファイルをほかのディレクリに置いたら,ファイルが見付かりませんとのエラーがありました、WindowsのC言語でカレントディレクトリを探すときは何の関数を使えばいいでしょうか? int main(void) { int i,k; int num; char filename[64],fileread[64],filewrite[64]; FILE *fp0,*fp1; double sum1,sum2,sum3; int *ch[3]; sum1=sum2=sum3=0.0; printf("ファイル名を入力ください!\n"); scanf("%s",filename); fprintf(stderr,"\n%s\n",filename); sprintf(fileread,"C:\\%s.txt",filename); fprintf(stderr,"%s\n",fileread); sprintf(filewrite,"C:\\%s.csv",filename); for (i=0;i<3;i++) { if ( (ch[i]=(int *)malloc(4*30))==NULL ) { fprintf(stderr,"Cannot get memory <ch[%d]>.",i); return -1; } } fprintf(stderr,"%s\n",filewrite); if ((fp0=fopen(fileread,"rb"))==NULL) { fprintf(stderr,"Cannot open file %s\n",fileread); return 0; } fscanf(fp0,"%d", &num); if((fp1=fopen(filewrite,"wb"))==NULL) { fprintf(stderr,"Cannot open file!%s\n",filewrite); return 0; } for(i=0;i<50;i++) { fscanf(fp0,"%d %d %d",*(ch[0]),*(ch[1]),*(ch[2])); } for(i=0;i<num/100;i++) { for (k=0;k<100;k++) { fscanf(fp0,"%d %d %d",*(ch[0]),*(ch[1]),*(ch[2])); if ( feof(fp0) != 0 ) break; sum1=sum1+*(ch[0]); sum2=sum2+*(ch[1]); sum3=sum3+*(ch[2]); } fprintf(fp1,"%d %d %d\n",sum1,sum2,sum3); } fclose(fp0); fclose(fp1); return 0; }

  • ファイル

    c言語初心者です。 どなたか教えていただけませんか? これだとiが定義されないことになってしまいます。 #include<stdio.h> #include<stdlib.h> int main() { FILE *fp; fp=fopen("monai", "w"); if(fp==NULL){ fprintf(stderr,"ファイルのオープンに失敗しました。\n"); exit(0); } int i; for(i=1;i<=30;i++) { if(i<=9){ fprintf(fp," %d ",i); } else if(i%10==0) { fprintf(fp,"%d\n",i); } else { fprintf(fp,"%d ",i); } } fclose(fp); return 0; } ビルドもできません。

  • VC++のfopenのファイル名の指定方法について

    下記のようなcsvファイルを間引くプログラムを改変したいと思っています。 環境はVS2008 C++ コンソールアプリケーションです。 #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { FILE *fp,*fp1; int i, j, retu, ch, cnt=0; char data[100]; char a=0; fp=fopen("data.csv","r"); fp1=fopen("kekka.csv","w"); cnt=0; retu=0; while (1){ ch=fgetc(fp); if (ch=='\n') break; } while((ch=fgetc(fp))!=EOF){ data[retu]=ch; retu++; if (ch=='\n'){ cnt++; if (cnt>=10){ cnt=0; for(i=0;i<retu;i++){ printf("%c",data[i]); fputc(data[i], fp1); } } retu=0; } } return 0; } ただ毎回csvファイルの名前をdata.csvに書き換え、さらにkekka.csvを別名にして保存しなければいけないためとても手間です。 そこでその部分を実行時にscanfなどを用いて変更したいと思ったのですがうまくいきません。 fopenでファイル名を実行時(ビルド時)に変更するにはどうすればよいのでしょうか。 ご教授お願いします。

  • ファイルから一文字ずつ読み込む

    ファイルを読み込むfgetc()関数のところでプログラムが停止します。 以下が実行したプログラムです。 #include<stdio.h> void get_name(char name[],int a){ printf("ファイル名を入力してください。\n"); scanf("%s",name); } void open(FILE *fp2,char name[]){ if((fp2=fopen(name,"r"))==NULL){ printf("ファイルオープンエラー"); } } int count(FILE *fp3){ int ch=0; int count=0; if(fp3==NULL){ printf("error"); } while((ch=fgetc(fp3))!=EOF){ if(ch=='\n'){ count++; } } printf("TEST"); fclose(fp3); return(count); } int main (void){ FILE *fp; char fname[30]; get_name(fname,30); open(fp,fname); printf("%d",count(fp)); return(0);} ファイル名を入力してください。ファイル名を入力、プログラム停止です。 '\n'を数えれるようにしてください。御指摘お願いします。

  • ファイル

    AからZまでの文字が何回出力されるか数えるプログラムなのですが、うまく出力されません。 どこを変えればよろしいでしょうか。 #include<stdio.h> #include<stdlib.h> #include<ctype.h> int count[26]; int main(void) { char str[100] = "xyzYZZ\n"; FILE *fp; char *p; int i; char ch; if((fp = fopen("myfile","w")) == NULL){ printf("ファイルを開くことが出来ません"); exit(1); } p = str; while(*p){ if(fputc (*p,fp) == EOF){ printf("ファイル書き込みエラー"); exit(1); } p++; } fclose(fp); if((fp = fopen ("myfile","r")) == NULL){ printf("ファイルを開くことが出来ません"); exit(1); } while((ch == fgetc(fp)) != EOF){ ch = toupper(ch); if( ch >= 'A' && ch <='Z' ) count[ch - 'A']++ ; } for( i=0 ; i<26 ; i++) printf("%c は %d 回出現\n",i + 'A', count[i]); fclose(fp); return 0; }

  • ファイルの読み書き

    ファイルに文字列を追加したいのですがファイルの開き方が "a" モードで開くと当然文章の最後に文字列が書き込まれることになります。 ですが、最後ではなく最初に文字列を追加したいためにファイルを2回づつ読み込んだり書いたりしたら2回目の読み込み時にバグが出ました どうしたらファイルの文字列を最後ではなく最初に追加できますか? 追加したい文字が: もも 今までのデーターが オレンジ みかん なら、 もも オレンジ みかん のようにしたい。 以下ソース ---ソース--- #include <stdio.h> void main(void){ FILE *fp; char *tm[1000]; char buf[400]; int i=1,sei; fp= fopen("now.txt","w+"); fprintf(fp,"もも"); fclose(fp); //何で最初に書き込んでるんだ? //という突っ込みがあるでしょうが本当に作りたいプログラムは最初にファイルに書き込まないといけないためです。 fp= fopen("now.txt","r"); while( fgets( buf, 400, fp ) != NULL ){ tm[0]=buf; } fclose(fp); printf("%s<br>",tm[0]); //確認用 この時点ではtm[0]に"もも"が入っている fp =fopen("moto.txt","r"); while( fgets( buf, 400, fp ) != NULL ){ tm[i]=buf; printf("%s<br>",tm[i]); //確認 この時点ではtm[1]に"オレンジ" tm[2]にみかんが入っている i++; } printf("%s<br>",tm[0]); //確認用 バグったtm[2]のみかんが入っている printf("%s<br>",tm[1]); //確認用 バグったtm[2]のみかんが入っている printf("%s<br>",tm[2]); //確認用 バグじゃない? fclose(fp); if(i<=1000){ sei=i; } else{ sei=1000; } fp =fopen("chat_deta.txt","w+"); for(i=0;i<sei;i++){ fprintf(fp,"%s\n",tm[i]); //バグった内容が書き込まれるためみかんが3列かきこまれる } fclose(fp); } } ---now.txt--- もも ---moto.txt--- オレンジ みかん 結果として みかん みかん みかん っとなっていますね。

  • fscanf関数について

    -------------------------------------------------- #include<stdio.h> #include<stdlib.h> int main() { FILE*fp; int ch,dt; char ss[80]; if((fp=fopen("bbb.txt","w"))==NULL){ printf("出力ファイルをオープンできません.\n"); exit(1); } fprintf(fp,"%c",'A'); fprintf(fp,"%s\n","abcdeABCDE"); fprintf(fp,"%d\n",1234); fclose(fp); if((fp=fopen("bbb.txt","r"))==NULL){ printf("入力ファイルをオープンできません.\n"); exit(1); } ch=fgetc(fp); printf("ch=%c\n",ch); fscanf(fp,"%s",ss); printf("ss=%s\n",ss); fscanf(fp,"%d",&dt); printf("dt=%d\n",dt); fclose(fp); return 0; } -------------------------------------------------- 以上のプログラムで、プログラムの通り「bbb.txt」は、 AabcdeABCDE 1234 となっております。 そこで疑問なのですが、「ch=fgetc(fp);」は1文字読み込みなので、'A'だけと分かるのですが、「fscanf(fp,"%s",ss);」はfpからの読み込みで何故、 AabcdeABCDE 1234 の全部を読み込まず、'A'を抜かした、「abcdeABCDE」だけを読み込んでくれるのか? 後、「fscanf(fp,"%d",&dt);」は何故「AabcdeABCDE」を抜かした、「1234」だけを読み込んでくれるのかが分かりません。 「fscanf(fp,"%d",&dt);」については数値だけを読み込んでくれるのかと思い、 ch=fgetc(fp); printf("ch=%c\n",ch); fscanf(fp,"%s",ss); printf("ss=%s\n",ss); の部分を無くせば、「1234」だけを読み込んでくれるのかと思ったのですが、数値は正しく表示されません。 以上教えていただければ嬉しいです。

  • バイナリファイルの読み込み(C言語)

    raw(音楽ファイル)データを配列rawに読み込みたいのですが,バイナリファイルの読み込み方がわかりません. サンプルで以下のようなソース(途中略)があるのですが, ・なぜrawの型としてshortを使っているのか ・データ数の半分(file_size = ftell(fp) / 2)しか読み込んでいない ・fgetc(fp) << 8 あたりの意味がわからないので教えて下さい. -------------------------------------------------------- short *raw; if((fp=fopen(argv[1], "rb")) == NULL){ fprintf(stderr, "can't open %s.\n", argv[1]); exit(1); } fseek(fp, 0, SEEK_END); file_size = ftell(fp) / 2; fseek(fp, 0, SEEK_SET); raw = (short *)malloc((size_t)(file_size * sizeof(short))); if(raw == NULL){ fprintf(stderr, "malloc error\n"); exit(1); } for(i=0;i<file_size;i++) raw[i] = (short)((fgetc(fp) << 8) | fgetc(fp)); -----------------------------------------------------

  • C言語、fgetcを利用しファイルの内容を変数に

    C言語について質問です C言語のfgetcを利用しファイルの内容を変数にいれてそれを返す関数を作っているのですがうまくいきません <!--以下ソース--> char *file_get_contents(char *filename){ FILE *fp; int c; char *return_str; if((fp=fopen(filename,"r"))==NULL) return NULL; while((c=fgetc(fp))!=EOF ){ sprintf(return_str,"%c",c); } fclose(fp); return return_str; } 誰か理由と改善方法を教えてください!

  • C言語→C#に変換

    C言語→C#に変換 C言語からC#に変換したいのですが、わからないところがあります。 コマンドライン引数からファイル名とキーワードを入力して、キーワードがある行をアウトプットファイルに書き込むという処理です。 C言語のソースは、 main(int argc,char *argv[]){ FILE *fp; FILE *fpp; int i; char KEYWORD[256]; char buf[256]; //(1)アウトプットファイルのオープン fpp=fopen("output.txt","w"); for(i=1;i<argc-1;i++){ if(strcmp(argv[i],"-a")==0){ //(2)入力ファイルのオープン if((fp=fopen(argv[i],"r"))==NULL){ printf("open error!\n"); exit(1); } } //(3)キーワードの代入 if(strcmp(argv[i],"-b")==0){ strcpy(KEYWORD,argv[i]); } } //(4)一行読み込む while(fgets(buf,sizeof(buf),fp)!=NULL){ //(5)キーワードの条件で文字列抽出 if(strstr(buf,KEYWORD)!=NULL){ //(6)アウトプットファイルに出力 fprintf(fpp,"%s",buf); } } //ファイルクローズ fclose(fp); fclose(fpp); } です。 (3)キーワードの代入と、(5)文字列抽出の部分がネットなどで調べてもよくわかりません。 分かる方いらっしゃいましたら教えていただけると幸いです。 宜しくお願いします。 .