- ベストアンサー
fscanf関数のscanf集合を使う時の不具合
- C言語でプログラミングをしている中で、fgetc、fputc、fgets、fputs、fprintfといった関数は呼び出されるたびにファイルの現在位置を進めるが、fscanf関数において、scanf集合を使う場合にはいつもファイルの先頭からスキャンが開始されるようです。
- 例えば、テキストファイルから英単語だけを取り出して順番に出力するために、fscanf関数を使用していますが、不具合のために正しく動作しません。試行錯誤してファイルの現在位置を手動で進める方法も試しましたが、上手くいきません。
- 便利なscanf集合を使いたいのですが、fscanf関数での不具合が困っています。上記のコードを正しく動作させる方法について、アドバイスをいただきたいです。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
"%[a-zA-Z]s" って「文字コードが a以上z以下もしくは A以上Z以下のものを『できる限り』読み込む」+「文字 s を読む」って意味だけど, 「文字 s」はその前の %[a-zA-Z] に含まれている (はず) なので最後の s は絶対に読み込めない (はず) です>#1. もちろん %[a-zA-Z] で英字以外のものを読み込んでしまう可能性はあります. さておき「仕様を確認しろ」には同意.
その他の回答 (2)
- asuncion
- ベストアンサー率33% (2127/6289)
>適当なテキストファイルtest.txt 中身を見せてください。 # scanf集合って何のことかと思ったら、スキャン集合もしくはスキャンセットのことだったのね。 # 'f'は不要。
お礼
御回答ありがとうございます。 test.txtの内容は以下の通りです。 ____________________________________________________________ :abc d1f;gh2-@;ijkLMN OPQ ;:RSTUあいうえお V3W4X5Y6Z ____________________________________________________________
- Wr5
- ベストアンサー率53% (2173/4061)
>fscanf関数において、scanf集合を使った場合だけは、 >何故かいつもファイルの先頭からスキャンを開始するようです。 改行含んだりしていないですよね? また、例に示したコードだとアルファベットしか許容しませんが。 >fscanf(fp, "%[a-zA-Z]", str); の戻り値は確認しましたか? # "%[a-zA-Z]s"じゃないんすかね? 仕様をちゃんと確認しましょう。 改行を含んだファイルなら改行文字の直前までしか読めませんし 連続するアルファベットが80文字を越えると楽しいコトが発生するプログラムです。 便利…ということでscanf()系使う場合は、入力が希望通りでなかった場合の挙動も確認した上で使いましょう。 int num = 1; while(num != 0){ scanf("%d", &num); } で数字以外を入力した場合にどうなるか、試したコトはありますか?
お礼
御回答ありがとうございます。 >改行含んだりしていないですよね? 含んでますけど、含んでいたら、まずいのでしょうか? 記述して頂いたコードブロックを参考にし、 int num = 1; while(num != 0){ scanf("%d", &num); printf("%d ",num); } を試したところ、数字以外の文字、例えばaを入力すると、 1 1 1 1 1...と出力されて、プログラムが止まりませんでした。 こういった経験はこれまでに何度もしてきたので、上手く行かない事は存じているのですが、今回の問題とは何の関係があるのでしょうか?
お礼
御回答ありがとうございます。 >さておき「仕様を確認しろ」には同意. 一応様々なサイトで仕様は確認したのですが、理解が甘いようです。 fscanf(fp, "%[a-zA-Z]", str); というコードは、 「test.txtの現在位置(ftell関数で返される所)から、内容をスキャンし、 アルファべットが見つかれば、次にアルファベットでない文字が現れる所までをstrに格納する。」 という動作をするのだと思うのですが、違うのでしょうか?
補足
自己解決できましたので、その時に行った作業を以下に記述しておきます。 回答して下さった皆さん、ありがとうございました。 test.txtの内容は以下の通りです。 ____________________________________________________________ :abc d1f;gh2-@;ijkLMN OPQ ;:RSTUあいうえお V3W4X5Y6Z ____________________________________________________________ プログラムを以下の様に修正すると上手く行きました。 char str1[80], str2[80]; while(!feof(fp)){ fscanf(fp, "%[^a-zA-Z0-9]%[a-zA-Z0-9]", str1, str2); printf("str1:「%s」\t ", str1); printf("str2:「%s」\n", str2); } 出力は以下のようになりました。 ____________________________________________________________ str1:「:」 str2:「abc」 str1:「 」 str2:「d1f」 str1:「;」 str2:「gh2」 str1:「-@;」 str2:「ijkLMN」 str1:「 」 str2:「OPQ」 str1:「 ;:」 str2:「RSTU」 str1:「あいうえお 」 str2:「V3W4X5Y6Z」 ____________________________________________________________ どうやら、fscanf関数も、読み込むたびにファイルの現在位置を進めるようです。 最初に記述していた fscanf(fp, "%[a-zA-Z]", str);というコードは、 「アルファベット以外の文字は全て捨て、連続したアルファベットのみをstrに格納する」 という動作をすると考えていたのですが、どうやら、捨てられた文字が入力バッファに残ったままになることで問題が発生していたようです。 この問題は、fflush(fp); というコードで、ループの度に入力バッファをフラッシュしても解決できませんでした。 そこで考え付いたのが、 「捨てられるべき文字を、他の文字列に格納する」 という方法です。 コードで表すとfscanf(fp, "%[^a-zA-Z0-9]%[a-zA-Z0-9]", str1, str2); の部分です。 これで、str2に英単語のみが格納されます。 ただし、%[^a-zA-Z0-9]%[a-zA-Z0-9]の部分を%[a-zA-Z0-9]%[^a-zA-Z0-9]とすると、上手く行きませんでした。 これは、test.txtが::という数字でもアルファベットでもない文字で始まっているからです。 この問題を解決するには、以下のようなコードブロックをwhileループの前に記述しておくことで解決できました。 ____________________________________________________________ char ch; do{ fscanf(fp, “%c”, &ch); }while( !isdigit(ch) && !isalpha(ch) ); fseek(fp, -1, SEEK_CUR); ____________________________________________________________ このコードブロックは、数字またはアルファベットが初めて現れる位置までファイルの現在位置を進め、 見つかった所から、現在位置を1つ戻すという作業を行っています。 最初の文字が数字またはアルファベット以外で始まるファイルにも対応できるようになります。