fscanf関数のscanf集合を使う時の不具合

このQ&Aのポイント
  • C言語でプログラミングをしている中で、fgetc、fputc、fgets、fputs、fprintfといった関数は呼び出されるたびにファイルの現在位置を進めるが、fscanf関数において、scanf集合を使う場合にはいつもファイルの先頭からスキャンが開始されるようです。
  • 例えば、テキストファイルから英単語だけを取り出して順番に出力するために、fscanf関数を使用していますが、不具合のために正しく動作しません。試行錯誤してファイルの現在位置を手動で進める方法も試しましたが、上手くいきません。
  • 便利なscanf集合を使いたいのですが、fscanf関数での不具合が困っています。上記のコードを正しく動作させる方法について、アドバイスをいただきたいです。
回答を見る
  • ベストアンサー

fscanf関数のscanf集合を使う時の不具合

こんにちは。 現在、C言語でプログラミングをしています。 最近気が付いたのですが、fgetc、fputc、fgets、fputs、fprintfといった関数は、 呼び出されるたびにファイルの現在位置を進めるのですが、 fscanf関数において、scanf集合を使った場合だけは、 何故かいつもファイルの先頭からスキャンを開始するようです。 例えば、適当なテキストファイルtest.txtを作成し、 ------------------------------------ FILE *fp; char str[80]; fp=fopen("test.txt", "r"); while(!feof(fp)){ fscanf(fp, "%[a-zA-Z]", str) puts(str); } ------------------------------------ といったコードブロックを試してみると、永遠にforループが終了せず、 最初の英単語が何回も画面に出力されます。 このコードブロックの目的は、test.txtから、英単語だけを取り出して、順番に出力していく事です。 上のコードブロックで、whileループの最後に、fseek(fp, srtlen(str), SEEK_CUR); といったコードを挿入し、ファイルの現在位置を手動で進めてみたのですが、 どうも上手く行きませんでした。 scanf集合は便利なので、fscanf関数で使っていきたいのですが、こういった不具合があるので困っています。 上のコードブロックを上手く動作させる方法だけでもいいので、何かアドバイスを頂きたいと思います。 では、よろしくお願い致します。

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

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

"%[a-zA-Z]s" って「文字コードが a以上z以下もしくは A以上Z以下のものを『できる限り』読み込む」+「文字 s を読む」って意味だけど, 「文字 s」はその前の %[a-zA-Z] に含まれている (はず) なので最後の s は絶対に読み込めない (はず) です>#1. もちろん %[a-zA-Z] で英字以外のものを読み込んでしまう可能性はあります. さておき「仕様を確認しろ」には同意.

MetalLover
質問者

お礼

御回答ありがとうございます。 >さておき「仕様を確認しろ」には同意. 一応様々なサイトで仕様は確認したのですが、理解が甘いようです。 fscanf(fp, "%[a-zA-Z]", str); というコードは、 「test.txtの現在位置(ftell関数で返される所)から、内容をスキャンし、 アルファべットが見つかれば、次にアルファベットでない文字が現れる所までをstrに格納する。」 という動作をするのだと思うのですが、違うのでしょうか?

MetalLover
質問者

補足

自己解決できましたので、その時に行った作業を以下に記述しておきます。 回答して下さった皆さん、ありがとうございました。 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つ戻すという作業を行っています。 最初の文字が数字またはアルファベット以外で始まるファイルにも対応できるようになります。

その他の回答 (2)

  • asuncion
  • ベストアンサー率33% (2126/6286)
回答No.2

>適当なテキストファイルtest.txt 中身を見せてください。 # scanf集合って何のことかと思ったら、スキャン集合もしくはスキャンセットのことだったのね。 # 'f'は不要。

MetalLover
質問者

お礼

御回答ありがとうございます。 test.txtの内容は以下の通りです。 ____________________________________________________________ :abc d1f;gh2-@;ijkLMN OPQ ;:RSTUあいうえお V3W4X5Y6Z ____________________________________________________________

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.1

>fscanf関数において、scanf集合を使った場合だけは、 >何故かいつもファイルの先頭からスキャンを開始するようです。 改行含んだりしていないですよね? また、例に示したコードだとアルファベットしか許容しませんが。 >fscanf(fp, "%[a-zA-Z]", str); の戻り値は確認しましたか? # "%[a-zA-Z]s"じゃないんすかね? 仕様をちゃんと確認しましょう。 改行を含んだファイルなら改行文字の直前までしか読めませんし 連続するアルファベットが80文字を越えると楽しいコトが発生するプログラムです。 便利…ということでscanf()系使う場合は、入力が希望通りでなかった場合の挙動も確認した上で使いましょう。 int num = 1; while(num != 0){  scanf("%d", &num); } で数字以外を入力した場合にどうなるか、試したコトはありますか?

MetalLover
質問者

お礼

御回答ありがとうございます。 >改行含んだりしていないですよね? 含んでますけど、含んでいたら、まずいのでしょうか? 記述して頂いたコードブロックを参考にし、 int num = 1; while(num != 0){ scanf("%d", &num); printf("%d ",num); } を試したところ、数字以外の文字、例えばaを入力すると、 1 1 1 1 1...と出力されて、プログラムが止まりませんでした。 こういった経験はこれまでに何度もしてきたので、上手く行かない事は存じているのですが、今回の問題とは何の関係があるのでしょうか?

関連するQ&A

  • scanf関数について

    こんなプログラムがありました。 char str[80]; while(scanf("%s",str)>=1){    ・・・  } このwhileループは何が入力されたときに終了するのですか?scanf関数は入力された文字を返り値に持つとわかったので、改行コードを入力しましたが、終了しませんでした。どうすればよいのでしょう。よろしくお願いいたします。

  • 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; }

  • fscanf

    fscanfを使って、ファイル(普通の英文が入っています)から一単語ずつ読み込んでいきたいと思っているのですが、どうすれば良いのかわかりません。 int main(int argc,char *argv[]){ FILE *fp; char *word; fp = fopen(argv[1], "r"); if(fp == NULL){ printf("error: not open file.\n"); return(0) ;} while( ){ fscanf(fp,"%[a-zA-Z]",word); printf("%s\n", word); fflush(stdout); } fclose(fp); return 0; } とりあえず上の様なプログラムで、一単語ずつ順番に単語を出力できるようにしたいと思っているのですが。 いろいろ変なところなど在ると思いますが、whileの条件など、どうすればよいか教えてください。

  • 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); } } 毎度すいませんがこの解決方法をご教授ください。よろしくお願いします。

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

  • scanfが実行されません

    Cについて質問です。 whileループに入る前にscanfでchar変数に文字を代入するようにして、その文字でwhileループの条件を設定したのですが、scanfが実行されずにすっ飛ばされてwhileループに入ってしまいます。 同じようなコードををdo-while文で書いてみたところ、一回目のscanfがやはりすっ飛ばされて二回目に入り、そこでscanfが実行されます。 エラーは出ません。何が間違っているのか、さっぱり分かりません。教えてください。

  • 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の間の数が表示されるよう、指摘をおねがいします。

  • fscanfで格納された変数がおかしいです。

    fscanfで格納された変数がおかしいです。 ダブルポインタで定義した変数にfscanfでファイルから文字列データを読み込んでいるのですが 表示がおかしな事になっています。 読み込んだファイルの内容は、 aiueo kakikueko sasisuseso tatituteto です。 表示された結果が kakikueko sasisuseso tatituteto kakisasitatituteto sasitatituteto tatituteto 自分では解決しずらいのでここで質問をさせて頂くことになりました。 C/C++で記述してあるのですが、以下にソースを載せておきますのでご指摘ください。 /* double pointa */ #include <stdio.h> #include <stdlib.h> #define MAXSIZE 256 char** size; // TEST void test(void) { for(int l = 0; l < 3; l++) { printf("\n%s", &size[l]); } } int main() { FILE* fp; int c = -1; char moji[MAXSIZE]; if((fp = fopen("test.txt","rb")) == NULL) { printf("error"); exit(1); } while(fscanf(fp,"%s",moji) != EOF) c++; size = (char**)malloc(sizeof(char) * MAXSIZE * c); // 初めに戻す fseek(fp, 0, SEEK_SET); // 最初の文だけ取り出す fscanf(fp,"%s",moji); // 一文を格納する for(int l = 0; fscanf(fp, "%s", &size[l]) != EOF; l++) { printf("\n%s", &size[l]); } printf("\n"); test(); fclose(fp); getchar(); free(size); return 0; } test関数内で表示するとおかしな結果がでるのですが何故かわかりません。 よろしくお願いします。

  • ループ中でのscanfおよびcin

    あまりに基礎的な質問ですが,ループ中の入力関数が期待する動作になりません. コードを載せます while (1) { int key; scanf("%d", &key); if (key == 1) break; } 期待する動作は1が入力されるまでループし続けるというものですが scanfは一度しか実行されずループし続けます,cinに変えても同様でした 何が原因か分かる方,ご教示ください

  • scanf の%10s について

    初歩すぎる質問でもうしわけありませんが、 str[100]という配列を用意して 無限ループ内に while(1){ printf("入力してください>>>"); scanf("%10s",str); } などとして、実行して 10文字以上入力すると、 「入力してください>>>」が一回ではなく 「入力してください>>>入力してください>>>」 のように複数でるのですが、なぜでしょうか?? なにがおきているのでしょうか??

専門家に質問してみよう