• ベストアンサー

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」だけを読み込んでくれるのかと思ったのですが、数値は正しく表示されません。 以上教えていただければ嬉しいです。

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

  • ベストアンサー
回答No.5

>%dは空白分を読み飛ばすとわかったのですが、以上のプログラムでも正常に動いたので、「%s」も空白文を読み飛ばしてくれるのですかね? 話が逆。 「空白(改行も含む)が読めるのは、%cだけ」ってのが本質。 「%cだけは、空白(改行も含む)の読み飛ばしをせず、今読める文字を1文字読むだけ」になっているのです。 今読める文字が空白だろうが改行だろうが1だろうがAだろうが、とにかく1文字読むだけです。 「%c」以外の「%d」や「%s」や「%何とか」は「まず、空白(改行も含む)を読み飛ばし、空白(改行も含む)以外の何かが出て来たら、読み込みを開始」します。そして「読み込めない文字(空白や改行も)が出て来たら、そこでヤメ」にします。 まとめると ・%cは、とにかく1文字読む。読み飛ばしとかはしない。 ・%c以外は、とにかく、空白と改行を読み飛ばしてから読み込みを開始し、空白や改行が出て来たら読み込みをヤメる。 scanf(やfscanf)は「上記の2点がすべて」です。 「%dは~」とか「%sは~」とか、書式指定子個別に悩んではいけません。本質を把握すれば「みんな一緒」で、悩む必要はありません。

muffler
質問者

お礼

ご回答ありがとうございます。 >・%cは、とにかく1文字読む。読み飛ばしとかはしない。 >・%c以外は、とにかく、空白と改行を読み飛ばしてから読み込みを開始し、 >空白や改行が出て来たら読み込みをヤメる。 以上の記述納得できました! ありがとうございます。

その他の回答 (4)

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

基本的には printf とか scanf とかを想定すればいいです. 例えば, printf で出力したときに[上書きされて困った」という経験はおそらくないと思います. 同様に, scanf でも「前に入力したはずのデータを読み込んでる」という体験はないですよね. それと同じで, fprintf で出力したら上書きされることはなく, fscanf で読み込んだときにも「既に読み込んでしまったデータ」は消えてしまうと思ってください. あと %s で改行を読み込むかどうかなんですが.... 一応 #3 では「読み込まない」と読めるように書いたつもり (「読み込みを停止した原因である改行文字は残ったままであり, 次の %d ではこの改行文字から読み始めることになります」のあたり) です. この辺は微妙なところですが, 変換指定 (%なんちゃら, ってやつ) は一部を除いて「先頭にある空白文字は全て読み飛ばす」「読み込めなかった文字は全て以降の読み込みのために残す」と思ってくれれば OK. 質問に挙がっているプログラムで「1234」が入力できたのは, 「その前に残っている改行が %d で読み飛ばされた」ためです. もっと微妙なのは %d の変換指定に対し「123a」なんていう入力をした場合で, これだと「123」までは読み込みますがそのあとの「a」が処理できないので残ったままになります. この状態で再度 %d の指定をしても, 当然ながら読み込めないのでやはり「a」が残ってしまいます. このとき, 「ちゃんと読み込めたかどうか」をチェックせず while でループを作ると無限ループ.

muffler
質問者

お礼

度々すいません。 fprintf(fp,"%c",'A'); fprintf(fp,"%d\n",1234); fprintf(fp,"%s\n","abcdeABCDE"); ch=fgetc(fp); printf("ch=%c\n",ch); fscanf(fp,"%d",&dt); printf("dt=%d\n",dt); fscanf(fp,"%s",ss); printf("ss=%s\n",ss); %dは空白分を読み飛ばすとわかったのですが、以上のプログラムでも正常に動いたので、「%s」も空白文を読み飛ばしてくれるのですかね?

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

.... なぜ fscanf の方だけ疑問に思ったんだろう. つまり, fscanf で「どうして抜かすのか」と思うのなら, fprintf でも同じように「なぜ最初に戻って書き直さないのか」と思っていいはずなんだよね. ところがこっちは「プログラムの通り」でスルー. 簡単に行ってしまうと, fscanf と scanf, fgetc と getchar はどちらも「後者が stdin 専用である」ことを除いて同じってこと. scanf や getchar のときに同じような疑問を持つことはない... よね? ただ, (特に) scanf系を「ちゃんと」使うには #2, #3 にあるように「内部でどのように動いているか」まで把握する必要があります.... おおっと. #3 の「fscanfの %s で指定された読み込みは 最初の空白文字(空白、タブ、改行のいずれか)までを読み込むので ABCDEの後ろにある改行が読み込み停止位置になります」というのは, 間違ってはいないんだけどその後に読み進めると誤解を招きそうな感じ. 「読み込み停止位置になる」のは正しいのですが, 読み込みを停止した原因である改行文字は残ったままであり, 次の %d ではこの改行文字から読み始めることになります. ただ, %d では「最初の方の空白文字は全部読み飛ばす」ので「1234 の 1 が読み込み開始位置になる」ように見えます. このように「読まなかった (or 読めなかった) 文字は残っている」という仕様により「scanf のあとで getchar を実行すると改行を読み込んでしまう」とか「ループの条件で scanf を使うときにちょっと間違えると想定外の入力で無限ループになる」とかいう事件を起こすことがあります.

muffler
質問者

お礼

ご回答ありがとうございます。 >fprintf でも同じように「なぜ最初に戻って書き直さないのか」と思って >いいはずなんだよね. 確かに以上の疑問もありました(;^_^) fprintf(fp,"%c",'A'); の後、 fprintf(fp,"%s\n","abcdeABCDE"); としても、'A'が上書きされないのは、「fprintf(fp,"%c",'A')」の後カーソルが、'A'の後ろにあるからですかね?? 後、ご指摘通り、改行はどうなるのかという疑問があります。 質問に挙げたプログラムが正常に表示されたのは、%sが改行も読み込んでくれたからなんですかね? Tacosanさんのご回答では以上の自分の考えはやはり違うような気がするんですけど。。 他の回答者様にも以下の質問をしたのですけど、 fprintf(fp,"%d\n",1234); fprintf(fp,"%c",'A'); fprintf(fp,"%s\n","abcdeABCDE"); により、 1234 AabcdeABCDE とし、 fscanf(fp,"%d",&dt); printf("dt=%d\n",dt); ch=fgetc(fp); printf("ch=%c\n",ch); fscanf(fp,"%s",ss); printf("ss=%s\n",ss); とすると、 dt=1234 ch=改行 改行 ss=AabcdeABCDE となってしまいます。 %dは改行も読み込んでくれないのですかね? よって対策として、 fscanf(fp,"%d",&dt); printf("dt=%d\n",dt); ch=fgetc(fp); ch=fgetc(fp); printf("ch=%c\n",ch); fscanf(fp,"%s",ss); printf("ss=%s\n",ss); 以上のように行わなければならないのですかね? %sが改行も読み込んでくれるのかという疑問も含めて教えていただけると嬉しいです。

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

ちゃんとリファレンス(ヘルプ)は確認したのでしょうか? fgetcやfscanfはファイルの現在の読み込み位置からデータを取得します fopenでファイルを開いた時点では 先頭のAにあるので 最初の fgetcは Aを返します 次の fscanfは abcd の aが読み込み開始位置ですから abcdeABCDE を読み込みます fscanfの %s で指定された読み込みは 最初の空白文字(空白、タブ、改行のいずれか)までを読み込むので ABCDEの後ろにある改行が読み込み停止位置になります 次の %d で 指定された fscanfは 前回の %sが ABCDEの後ろの改行まで処理しているので 1234の 1が読み込み開始位置になります ファイルを開いて 1234 を読み込むのであれば その前の読み捨てる部分をコード化しないといけませんよ たとえば fp = fopen("bbb.txt", "r"); fscanf( fp, "%*s%d", &dt ); fclose( fp ); といった具合に %*s で読み捨てを指示するなど ...

muffler
質問者

お礼

ご回答ありがとうございます。 >%sが ABCDEの後ろの改行まで処理しているので 1234の 1が読み込み開始 >位置になります 以上の記述理解できました。 例えば、 fprintf(fp,"%d\n",1234); fprintf(fp,"%c",'A'); fprintf(fp,"%s\n","abcdeABCDE"); により、 1234 AabcdeABCDE とし、 fscanf(fp,"%d",&dt); printf("dt=%d\n",dt); ch=fgetc(fp); printf("ch=%c\n",ch); fscanf(fp,"%s",ss); printf("ss=%s\n",ss); とすると、 dt=1234 ch=改行 改行 ss=AabcdeABCDE となってしまいます。 %dは改行まで読み込んでくれないのですかね? よって対策として、 fscanf(fp,"%d",&dt); printf("dt=%d\n",dt); ch=fgetc(fp); ch=fgetc(fp); printf("ch=%c\n",ch); fscanf(fp,"%s",ss); printf("ss=%s\n",ss); 以上のように行わなければならないのですかね?

  • bkbkb
  • ベストアンサー率33% (97/289)
回答No.1

何かの課題とかではないですよね? 多分… 簡単です。 ch=fgetc(fp); でA読み込んだので、Aの次のところにカーソルが居ます。 よって、次の fscanf(fp,"%s",ss); の時は、Aの次から1行読み込むので、Aを抜いて改行まで、つまり abcdeABCD になります。 そして、最後に1のところにカーソルが来ているので fscanf(fp,"%d",&dt); で1234が読み込まれます。 だから最初の2回の読み込みをなくせば、一番最初のところから読み込もうとするので、AabcdeABCDを数値として読もうとします。 だからおかしくなります。

muffler
質問者

お礼

ご回答ありがとうございます。 まだ初心者なため、ビギナー向けの参考書に記載されていたものです。 改行まで読み込みカーソルが移動していくという事理解できました! ありがとうございました。

関連するQ&A