• ベストアンサー

fscanfでループしてしまう。

大変お世話になっております。 C言語についてお聞きしたいことがあります。 テキストファイルをデータとして読み込み、その数などを計算した結果をテキストファイルにしたいのです。簡略して次のような手順を歩みたいと思います。catalog.txtは数行からなり、各行の34文字目から38文字目までが実数で、これを抽出して各行の実数データを計算したいのですが、作成されたファイルは一行目の計算が無限にループしています。 以下がプログラムです。 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> main(){ FILE *fp, *fp2; char str[1024]; char buf[256]; double i, kekka; if((fp = fopen("catalog.txt", "r")) == NULL){ printf("file open error1\n"); exit(1); } if((fp2 = fopen("kekka.txt", "w")) == NULL){ printf("file open error2\n"); exit(1); } while( fscanf(fp, "%[^\n]", str) != EOF ){ strncpy(buf, &str[33], 5); buf[5] = '\0'; i = atof(buf); kekka = 2 * i; fprintf(fp2, "%lf\n" ,kekka); } } 毎度すいませんがこの解決方法をご教授ください。よろしくお願いします。

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

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.4

★私も素直に『fgets』関数を使うべきだと思います。 ・ソースや他の回答者の方より、行単位で読み込んで行単位で処理していますので、  『fgets』関数を使う方が良いと思います。 ・それから、各行の 34 文字目から 38 文字目が実数でこれを抽出したいのならば、  今回の場合に限っては『strncpy』関数を使わなくても出来ます。  つまり、39 文字目に NULL 文字をセットして 34 文字目のアドレスから1つの実数  文字列として『atof』に渡せます。 ・下に『fgets』関数を利用した場合と抽出法のサンプルを載せます。 サンプル: FILE *fp, *fp2; char str[ 1024 ]; : 中略 : while ( fgets(str,sizeof(str),fp) != NULL ){ ←行単位で文字列を読み込む  str[ 38 ] = '\0'; ←39 文字目に NULL 文字をセット  i = atof( str + 33 ); ←『atof( &str[33] );』でも良い  kekka = 2 * i;  fprintf( fp2, "%lf\n" ,kekka ); } 最後に: ・ちょっとしたアドバイスで、英語で『結果』は『result』という単語があります。  ローマ字の『kekka』が分かりやすければいいですが、英語で統一するならば『result』の  単語を使ってみましょう。 ・あと『fgets』関数はエラーが起きた場合も NULL をリターンしますので、エラー対策を  行いたい場合は NULL が返された時に while 文を抜ける以外に『ferror(fp);』関数で  エラーかどうかを調査して下さい。→『feof(fp);』でファイルの最後(EOF)かも調査できます。 ・以上。おわり。→私は『fscanf』よりも『fgets』関数の利用をお勧めします。

参考URL:
http://oshiete1.goo.ne.jp/qa2625434.html
downboy
質問者

お礼

ご丁寧な解説ありがとうございました。 直接渡す方法も大変参考になりました。元のソースでははいくつかの列があり、それぞれ読み込む必要があります。 kekkaのご指摘のほうもありがとうございます。元のソースを改変して簡略化しましたが、不十分になってしまい、申し訳ありませんでした。<math.h>もこれでは必要ないですしね・・・。

その他の回答 (4)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.5

> 各行の34文字目から38文字目までが実数で それがわかっているのなら、わざわざfgetsを使って処理を煩雑にしなくても、 fscanf(fp, "%*33c%5lf%*[^\n]%*c", &i); とすれば、一発でかたがつきます。 fgetsを用いた方が細かなエラーチェックができますが、その分、コードは煩雑になります。ただし、細かなエラーチェックをするつもりなら、atofは論外なのでお勧めしません。strtodにしましょう。 また、printf系でdoubleを出力するときの書式は%lfではなく%fです。(あまりにも間違う人が多いので、C99では%lfもOKになってしまいましたが...)

回答No.3

「%[^\n]」とすると、改行の直前まで読み取ってくれます。 直前なんで改行は読み取りませんし、 次に読み込む文字が改行だと、何も読み込みません。 なので、入力バッファに改行文字が入ったままになるので、 何も読み込まない作業をずっと続けてしまいますね。 私も空白を読み込みたいために、この手法をたまに使います。 そのときは「%[^\n]%*c」という記述を使っています。 改行まで読み込んで、改行文字はスキップするというものです。 一番良い方法かどうかはわかりませんが、少しでも参考になればと思います。

downboy
質問者

お礼

早速のご回答ありがとうございます。 そういう性質なのですね。 代案も参考になりました。

noname#29127
noname#29127
回答No.2

>while( fscanf(fp, "%[^\n]", str) != EOF ){ の部分の"%[^\n]"はどういったことをしようとしているの でしょうか? %sとかではだめなのでしょうか? 一行の途中に空白がある場合で、行あたりの文字数が固定なら 下記のようなfgets使用のものはいかがでしょうか? while( fgets(str, 128, fp) >0 ){ strncpy(buf, &str[33], 5); buf[5] = '\0'; i = atof(buf); kekka = 2 * i; fprintf(fp2, "%lf\n" ,kekka); }

downboy
質問者

お礼

早速のご回答ありがとうございます。 説明不足でしたが、お察しの通り、一行には文字、数字、空白があったのでこうしました。 fgetsを試してみたいとおもいます。

  • mac_res
  • ベストアンサー率36% (568/1571)
回答No.1

while( fscanf(fp, "%[^\n]", str) != EOF ){ で読み残した、\nが永久に残ります。 素直にfgets()を使うべきです。 while (fgets(str, 1024, fp) != 0) {

downboy
質問者

お礼

早速のご回答ありがとうございます。 やはりそこでしたか。 fgetsも検討していましたが、fscanfのほうが使いやすそうだったので・・・ 早速試してみたいと思います。

関連するQ&A

  • fscanf()

    fcanf()がうまくいきません。 画面には、42640888のような数値が表示されます。 どこが間違ってるか教えてください。 (test.txtに23と書いてあるとします。) #include<stdio.h> int main(void) {    FILE *fp;    FILE *fp1;    char str[10];    int a;    fp=fopen("test.txt","r")    fp1=fopen("a.txt","w")    while(!feof(fp)){       fgets(str,8,fp);       fputs(str,fp1);    }    fscanf(fp,"%d",&a);    printf("%d",a);   fclose(fp);   fclose(fp1);   return 0; }

  • C言語でセグメンテーションエラーの原因がわからず困っています。

    こんにちは。いつもお世話になっております。 C言語なのですが、何故かプログラムを実行するとセグメンテーション違反が表示されてしまい全く動きません。皆さんのお力を貸して頂けないでしょうか。 以下が問題のプログラムです。 前提条件: ・別プログラムによりfile.txtは末尾に文章が追加され続けている ・本プログラムは永劫的に動き続ける #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<errno.h> #include<signal.h> #include<sys/types.h> int main () { int i = 1, a; FILE *fp, *fp2; char *tok; //NULLを入れて初期化 char buf[1024] = ""; char str[1024] = ""; char str2[1024] = ""; //出力するファイルを開く if ((fp2 = fopen ("out.txt", "a")) == NULL) { printf ("out.txt:open error\n"); exit (-1); } while (1) { //何度もfopenするのでループ二週目以降は一旦fpを閉じる if (str2 != NULL) { fclose (fp); } //一度読み込み、2週目以降もさらに読み込み続ける if ((fp = fopen ("file.txt", "r")) == NULL) { printf ("file.txt:open error\n"); exit (-1); } //str2はループ2週目以降で使われる if (str2 != NULL) { while (1) //新しい行を見つけ出す { fgets (str, 1024, fp); if (strstr (str, str2) != NULL) { printf ("前回の最終行です:%s\n", str); break; } } } else { printf ("str2はNULLです:%s\n", str2); } //fopenで読み込んだ現段階のテキストファイルをout.txtに移す while (1) { if( (fgets (str, 1024, fp) ) == NULL)break; if ((a = strlen (str)) >= 2) { str[a - 1] = '\0'; } else { printf ("抽出した文字列が1文字以下です:%s\n", str); break; } if (strlen (str) >= 17) { strcpy (str2, str); } else { printf ("抽出した文字列が16文字以下です:%s\n", str); break; } printf ("%s\n", buf); fprintf (fp2, "%s\n", buf); strcpy (buf, ""); strcpy (str2, str); } printf ("これが検索語です:%s\n", str2); } fclose (fp2); return 0; } プログラムの内容は、 更新され続けるテキストファイル(file.txt)から、別のテキストファイル(out.txt)に出力するというプログラムです。(file.txtの内容を若干変えつつ、out.txtに出力することが本来の目的ですが、その部分は省略させて頂きました。) fopenで開くと、「その時点まで」のfile.txtしか開かれません。更新され続けているfile.txtの内容をコンスタントに読み取る為に、while文でfopenし続ける方法を取っていますが、そうするとout.txtに移し終わった行まで読み込んでしまい重複した文章をout.txtに移すことになりますので、strstrを使い、「前回fopenで開いたfile.txt」の最終行を検索してその位置からまたout.txtに移す、という方法を取りました。しかし、結果は何故かセグメンテーション違反でした。 以上のプログラムや私の意図する所で何か気づかれた点や、おかしいと思われた点等ありましたら是非ご教授頂きたく思います。 どんな些細なことでも構いませんので、教えて頂けないでしょうか。 環境はCentosです。どうぞよろしくお願いします。

  • fscanf ファイルから数を読み込む。

    ファイルから数を読み込むと 4201696 4201696 4201696 と、sample.txtにない数が表示されます。 sample.txtの中身は、2から6の数です。 sample.txtの中身は画像に添付しました。 以下は実行したプログラムです。 #include<stdio.h> #include <assert.h> int main(void){ FILE *fp; int a,b,i; if((fp=fopen("sample.txt","a"))==NULL){ printf("fileopen error\n"); } printf("整数を入力してください。"); scanf("%d",&a); fprintf(fp,"%3d\n",a); printf("整数を入力してください。"); scanf("%d",&a); fprintf(fp,"%3d\n",a); i=0; while(i<3){ fscanf(fp, "%d",&b); assert(b>2); printf("%3d\n",b); i++; } return(0);} 2から6の間の数が表示されるよう、指摘をおねがいします。

  • コンパイルエラーの原因がわからず困っています。

    こんばんは。 どなたか以下のプログラム(test4.c)が何故コンパイル出来ないのか教えて頂けませんか。行数は見やすいように一時的につけさせて頂きました。 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<unistd.h> 5 #include<errno.h> 6 #include<signal.h> 7 #include<sys/types.h> 8 9 10 11 12 13 int main(){ 14 FILE *fp,*fp2; 15 char str[1024]; 16 char *tok; 17 char buf[256]; 18 fp = fopen("file.txt","r"); 19 int i = 0; 20 fp2 = fopen("out.txt","a"); 21 22 while((str = fgets(str,1024,fp)) == EOF){ 23 24 while(1){ 25 // 前文を取得 26 tok = strtok(str, "("); 27 printf("%s\n",tok); 28 strcat(buf, tok); 29 30 // 中文を取得 31 tok = strtok(NULL, ")"); 32 if(tok == NULL)break; 33 tok = strtok(NULL, ")"); 34 if(tok == NULL)break; 35 36 *(tok + 1) = '\0'; 37 tok = strtok(NULL, "CEUFRSAP.");//フラグをトークンの材料にする 38 strcat(buf,tok); 39 40 // 後文を取得 41 tok = strtok(NULL, "("); 42 tok = strtok(NULL, ")"); 43 strcat(buf,"tcp "); 44 strcat(buf,tok); 45 break; 46 } 47 printf("%s\n",buf); 48 printf("%d行目です/n",i++); 49 fputs(buf,fp2); 50 } 51 52 printf("合計%d行です\n",i); 53 fclose(fp); 54 fclose(fp2); 55 return 0; 56 } 以下がコンパイルエラーの全文です。 test4.c: In function ‘main’: test4.c:22: error: incompatible types in assignment どんな些細な意見でも結構です。アドバイスをして頂けないでしょうか。どうぞよろしくお願いします。

  • テキストファイルへ半永久的に出力し続けることは可能でしょうか。

    こんにちは。いつもお世話になっております。 C言語で、半永久的に文字列をテキストファイルに出力し続ける、という事は可能でしょうか。要は、ファイルを無限に更新し続けるプログラムを書きたいと考えています。元のファイル(file.txt)の文字列を、形式を変えて、別のファイル(out.txt)に出力し続けたいのですがどうしても出力が止まってしまい上手くいきません。自分なりに以下のようなプログラムを作ってみました。何かアドバイスを頂けないでしょうか。 前提条件: ・別プログラムによりfile.txtは末尾に文章が追加され続けている ・本プログラムは半永久的に動き続ける #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<errno.h> #include<signal.h> #include<sys/types.h> int main () { int i = 0, j = 0, len; FILE *fp, *fp2; char buf[1024] = ""; char str[1024] = ""; while (1) { //ループ2周目以降なら一度クローズして開き直す if (i >= 1) { fclose (fp2); } if ((fp2 = fopen ("out.txt", "a")) == NULL) { printf ("out.txt:open error\n"); exit (-1); } //上と同じく if (i >= 1) { fclose (fp); } if ((fp = fopen ("file.txt", "r")) == NULL) { printf ("file.txt:open error\n"); exit (-1); } //既にout.txtに移した部分が重複しないようにfgetsで調整 if (i >= 1) { for (j = 0; j <= i; j++) { fgets (str, 1024, fp); } } //file.txtからout.txtに内容を移す while (1) { i++; if ((fgets (str, 1024, fp)) == NULL) break; if ((len = strlen (str)) >= 2) { str[len - 1] = '\0'; } else { break; } if (len <= 50) break; fprintf (fp2, "%s\n", str); } } return 0; } プログラムの内容は、 更新され続けるテキストファイル(file.txt)から、別のテキストファイル(out.txt)に出力するというプログラムです。(元となるfile.txtの内容を若干変えつつ、out.txtに出力することが本来の目的ですが、その部分は省略させて頂きました。) fopenで開くと、「その時点まで」のfile.txtしか開かれません。従って更新され続けているfile.txtの内容をコンスタントに読み取る為に、while文でfopenし続ける方法を取りました。しかし、out.txtを確認すると何故か一定の行からそれ以降が追記されないままでした。恐らく一度目のfopenにより開いたfile.txtの内容のみを抽出して、2度目以降のfopenによる読み取りが意味を為していないのではないかと推測しますが・・、どうなんでしょう。あまり自信はないです。 これを解決して半永久的に出力を続けるにはどうすればよいでしょうか。 上のプログラムや私の意図する所で何か気づかれた点や、おかしいと思われた点等ありましたら是非ご教授頂きたく思います。 どんな些細なことでも構いませんので、教えて頂けないでしょうか。 環境はCentosです。どうぞよろしくお願いします。

  • 同時にファイル読み込み 書き込み

    現在、ヒストグラムのプログラムを作成しています。 まず0~255の1000個の乱数ファイルdata.txtを読み込み、 ヒストグラムは出来たのですが、 エクセルでグラフを作りたいので、 data1.txtに書き込みたいので、下のソースでやってみましたが、 0~255のカウントが全部0になってしまします。 fp = fopen("data1.txt","w");が無ければ正常に処理されます。 どうか教えてください。よろしくお願いします。 #include <stdio.h> #define BUF 10 #define MAX 256 void count(FILE *fp , int* counter); int main(void) { FILE *fp; fp = fopen("data.txt","r"); fp = fopen("data1.txt","w"); int counter[MAX]; int i; for(i=0 ; i<MAX ; i++) { counter[i] = 0; } count(fp , counter); for(i=0 ; i<MAX ; i++) { printf("%d %d\n" , i, counter[i]); } fclose(fp); return 0; } void count(FILE* p_file , int* counter) { char buf[BUF]; while (fgets(buf , BUF , p_file) != NULL) { int n; sscanf(buf , "%d" , &n); counter[n]++; } }

  • ファイルに文字列を書く

    ファイルを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]=(char*)malloc(strlen(buf)+1); strcpy(tm[0], buf); } fclose(fp); fp =fopen("moto.txt","r"); while( fgets( buf, 400, fp ) != NULL ){ if(i<999){ tm[i] = (char*)malloc(strlen(buf+1)); strcpy(tm[i], buf); i++; } else{ tm[999] = (char*)malloc(strlen(buf+1)); strcpy(tm[999], buf); } } 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]); } if(i!=(sei-1)){ fprintf(fp,"\n"); } for(i=0;i<sei;i++){ free(tm[i]); } fclose(fp); } } ---now.txt--- もも ---moto.txt--- オレンジ みかん

  • cプログラミングについて

    以下はsample.txtというファイルを読み込み、辞書順に並べるプログラミングですが、どう正しく 直したらよいかわかりません。間違っている場所を指摘していただけたらと思います。 (間違えだらけで申し訳ありません) #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXLINE 500 void mysort(char *word[MAXLINE]) { int i,j; char *tmp; for(i=0;;i++){ for(j=i+1;; j++){ if(strcmp(word[j],word[i])==1){ tmp=word[i]; word[i]=word[j]; word[j]=tmp; } } } } int main(void) { int i; FILE *fp; char str[MAXLINE]; fp= fopen("sample.txt", "r"); if (fp == NULL) { printf("fopen error\n"); exit(1); } while(( fgets( str, MAXLINE, fp )) != NULL) mysort(str); for(i=0;; i++) printf("%s\n", str[i]); return 0; }

  • 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言語  2つのファイルを行ごとに読み込むプログラミング

    c言語  2つのファイルを行ごとに読み込むプログラミング 0.txt と 1.txt という2つのテキストフォルダがあり 0.txt の中身は a a b b 1.txt の中身は c c d d というものとします。 これら2つのフォルダを読み込むとき まず1つのフォルダの1行目(a a)を表示し 他方の1行目(c c) 2行目(d d)を表示させて 続いて1つのフォルダの2行目(b b)を表示し 他方の1行目(c c) 2行目(d d)を表示させたいのです。 つまり実行結果が a a c c a a d d b b  ←理想の実行結果です c c b b d d となるようにしたいのですが #include <stdio.h> #include <stdlib.h> #define STR_MAX 256 int main(void) { FILE *fp, *fp2; int i, j, k; char buf[STR_MAX]; char buf2[STR_MAX]; fp = fopen("0.txt", "r"); fp2 = fopen("1.txt", "r"); if (fp == NULL && fp2 == NULL){ printf("\n"); } while(fgets(buf, STR_MAX, fp) != NULL){ while(fgets(buf2, STR_MAX, fp2) != NULL){ printf("%s%s", buf,buf2); } printf("\n"); } fclose(fp); fclose(fp2); return 0; } このプログラミングの実行結果は a a c c a a d d となり、0.txtの2行目(b b)は表示されません。 おそらく while 文 を2重にすることで 不具合が起きているのだと思うのですが 色々と調べた結果、これ以外に プログラミングが思いつきません。 私の理想の実行結果にするためには どこを訂正させると良いのでしょうか? 恐れ入りますが ご回答 どうかよろしくお願いいたします。

専門家に質問してみよう