• ベストアンサー

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