【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板2こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.20131

入力バッファに残ったデータを破棄したい
投稿者---chu-(2005/02/23 16:29:51)


環境:LSI-C

scanfやfgets等、入力関数の実行後にバッファにデータが残ってしまうと、
次の入力関数に影響が出てしまうのを防ごうとしています。
以下の用に、fflushを使うと期待通り動いてくれました。
しかし、fflushを調べたところ、入力ストリームを与えたときの動作は未定義とのことでした。
定義されている方法で同じことをするにはどうすればよいでしょうか。
[ソース]
#include <stdio.h>

int main(void)
{
    char buf[4];

    printf("buf[4]:");
    fgets(buf, 4, stdin);
    fflush(stdin);
    printf("%02X,%02X,%02X,%02X\n", buf[0], buf[1], buf[2], buf[3]);

    printf("buf[4]:");
    fgets(buf, 4, stdin);
    fflush(stdin);
    printf("%02X,%02X,%02X,%02X\n", buf[0], buf[1], buf[2], buf[3]);

    return 0;
}

[実行結果]
buf[4]:12345
31,32,33,00
buf[4]:98    ←最初の入力での"45\x0A"が入り込まずに期待通り動作している
39,38,0A,00



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:入力バッファに残ったデータを破棄したい 20133 やっさん 2005/02/23 16:44:46
<子記事> Re:入力バッファに残ったデータを破棄したい 20138 Blue 2005/02/23 18:09:01
<子記事> Re:入力バッファに残ったデータを破棄したい 20144 shu 2005/02/23 22:03:55
<子記事> Re:入力バッファに残ったデータを破棄したい 20145 RAPT 2005/02/24 00:14:53
<子記事> Re:入力バッファに残ったデータを破棄したい 20148 RiSK 2005/02/24 10:14:15
<子記事> Re:入力バッファに残ったデータを破棄したい 20152 chu- 2005/02/24 15:35:47


No.20133

Re:入力バッファに残ったデータを破棄したい
投稿者---やっさん(2005/02/23 16:44:46)


私は次のような関数を作って,
毎回この関数を用いて値を入力しています.
input_int("aの値を入力>>>", &a);


配列を用意して,とりあえず改行まで全部読み込んでおいて,
それからatoiで数字だけを取り出します.

void input_int(char *text, int *n)
{
    char array[10];

         printf("%s\n", text);

    fgets(array, sizeof(array), stdin);
    *n  = atoi(array);
}



この投稿にコメントする

削除パスワード

No.20138

Re:入力バッファに残ったデータを破棄したい
投稿者---Blue(2005/02/23 18:09:01)


fgetsはstdinの場合最後に改行文字が入るのでそれを利用して、
改行文字の出現したらfgetsをやめるみたいなことをすればいいかな。

>しかし、fflushを調べたところ、入力ストリームを与えたときの動作は未定義とのことでした。
MSDNの fflushのサンプル見たら、普通に fflush( stdin ); 使ってました。

以下参考に。(私のソースは信頼できませんがw)

#include <string.h> #include <stdio.h> #define BUFF_SIZE 64 void stdinflush( const char* _pszBuff ) { char szFlushBuff[ BUFF_SIZE ] = { 0 }; if ( strchr( _pszBuff, "\n" ) != NULL ) { return; } do { fgets( szFlushBuff, BUFF_SIZE, stdin ); } while ( strchr( szFlushBuff, '\n' ) == NULL ); } int main( void ) { char buf[4]; printf("buf[4]:"); fgets(buf, 4, stdin); /* fflush(stdin); */ stdinflush( buf ); printf("%02X,%02X,%02X,%02X\n", buf[0], buf[1], buf[2], buf[3]); printf("buf[4]:"); fgets(buf, 4, stdin); /* fflush(stdin); */ stdinflush( buf ); printf("%02X,%02X,%02X,%02X\n", buf[0], buf[1], buf[2], buf[3]); return 0; }



この投稿にコメントする

削除パスワード

No.20140

Re:入力バッファに残ったデータを破棄したい
投稿者---Blue(2005/02/23 18:12:03)


>   if ( strchr( _pszBuff, "\n" ) != NULL )
リテラル間違ってました。orz
if ( strchr( _pszBuff, '\n' ) != NULL )




この投稿にコメントする

削除パスワード

No.20144

Re:入力バッファに残ったデータを破棄したい
投稿者---shu(2005/02/23 22:03:55)


fputs( "", stdin );

実際にためしていませんが、
こういうやり方はありでしょうか?

fputs( NULL, fp);

の方がいいのかな?


この投稿にコメントする

削除パスワード

No.20145

Re:入力バッファに残ったデータを破棄したい
投稿者---RAPT(2005/02/24 00:14:53)


rewind


この投稿にコメントする

削除パスワード

No.20148

Re:入力バッファに残ったデータを破棄したい
投稿者---RiSK(2005/02/24 10:14:15)


データがバッファに残っていることが条件ですが,私は↓を使っています。
int stdin_clear(void) {
    int c;
    while ((c = getchar()) != '\n') {
        if (c == EOF) return 0;
    }
    return 1;
}



この投稿にコメントする

削除パスワード

No.20152

Re:入力バッファに残ったデータを破棄したい
投稿者---chu-(2005/02/24 15:35:47)


皆さん返信ありがとうございます。

'\n'を取り込んでいるかどうかでバッファにデータが残っているか判断する方法は、
ほんとに'\n'だけで判断して大丈夫なのかがすこし不安ですし、
下記ソースのようにfscanfの場合に調べられないのでそれ以外を実験させていただきました。

結果、rewind(stdin)とfseek(stdin, 0L, SEEK_SET)がうまくいきました。
BCBのrewindヘルプを転載します。
---
ファイルポインタを,ストリームの先頭に移動します。
rewind(stream) は,fseek(stream, 0L, SEEK_SET) と同じです。
ただし,rewind はファイル終了標識とエラー標識をクリアしますが,
fseek はファイル終了標識のみをクリアします。
更新用にオープンされたファイルでは,rewind を呼び出した後は,
入力と出力のどちらでもできます。
SEEK_SET 0 ファイルの先頭
SEEK_CUR 1 現在のファイルポインタ位置
SEEK_END 2 ファイルの終わり
---
ここで疑問が発生しました。
バッファを破棄するのであればSEEK_SETではなくSEEK_ENDが適切であるように思えるのです。
SEEK_SETで先頭に戻すと、すでに読み込んだデータをまた読み込んでしまいそうです。
しかし、SEEK_ENDで試すと結果は×でした。
どのように考えればよいのでしょうか。
[ソース]
#include <stdio.h>

int main(void)
{
    int a, b;
    char buf[4];

    printf("a,b:");
    fscanf(stdin, "%d %d", &a, &b);
/*  fflush(stdin);              /* 1.未定義方法 */
/*  fputs("", stdin);           /* 2.結果×     */
/*  fputs(NULL, stdin);         /* 3.結果×     */
    rewind(stdin);              /* 4.結果○     */
/*  fseek(stdin, 0L, SEEK_SET); /* 5.結果○     */
/*  fseek(stdin, 0L, SEEK_END); /* 6.結果×     */
    printf("%d,%d\n", a, b);

    printf("buf[4]:");
    fgets(buf, 4, stdin);
/*  fflush(stdin);              /* 1.未定義方法 */
/*  fputs("", stdin);           /* 2.結果×     */
/*  fputs(NULL, stdin);         /* 3.結果×     */
    rewind(stdin);              /* 4.結果○     */
/*  fseek(stdin, 0L, SEEK_SET); /* 5.結果○     */
/*  fseek(stdin, 0L, SEEK_END); /* 6.結果×     */
    printf("%02X,%02X,%02X,%02X\n", buf[0], buf[1], buf[2], buf[3]);

    return 0;
}

[実行結果]
a,b:1 2 3
1,2
buf[4]:98   ←最初の入力での" 3\x0A"が入り込まずに期待通り動作している
39,38,0A,00



この投稿にコメントする

削除パスワード

No.20156

Re:入力バッファに残ったデータを破棄したい
投稿者---RiSK(2005/02/24 16:09:14)


>'\n'を取り込んでいるかどうかでバッファにデータが残っているか判断する方法は、
>ほんとに'\n'だけで判断して大丈夫なのかがすこし不安ですし、

規格で行単位でバッファリングされる事が書いてありませんでしたっけ?

>下記ソースのようにfscanfの場合に調べられないのでそれ以外を実験させていただきました。

調べられない? 何が?

>結果、rewind(stdin)とfseek(stdin, 0L, SEEK_SET)がうまくいきました。

いずれも処理系依存(規格上未定義)。
リダイレクトの時にもうまくいくのでしょうか?


この投稿にコメントする

削除パスワード

No.20157

Re:入力バッファに残ったデータを破棄したい
投稿者---RiSK(2005/02/24 16:25:58)


>規格で行単位でバッファリングされる事が書いてありませんでしたっけ?

調べました。
7.19.2 ストリーム
(snip...)
 テキストストリームは,行(line)を構成する順序づけされた文字の並びとする。各行は,0個以上の文字に行の終わりを示す改行文字を付加したものから成る。テキストストリームの最終行が,終端を示す改行文字を必要とするかどうかは,処理系定義とする。
stdin がテキストストリームであるという記述は見つけられませんでしたが,
おそらくstdinはテキストストリームでしょう。
改行文字を付加したものである以上,'\n'(とEOF)で判断するのは妥当だと思うのだけど。


この投稿にコメントする

削除パスワード

No.20158

Re:入力バッファに残ったデータを破棄したい
投稿者---RiSK(2005/02/24 16:39:03)


# 一人でツリーを肥大化させてごめんなさい。

>stdin がテキストストリームであるという記述は見つけられませんでしたが,

ありました。同じく 7.19.2 より
 プログラム開始時に三つのテキストストリームがあらかじめ定義されていて,明示的にオープンする必要がない。それらは,(通常の入力を読みとるための)標準入力(standard input),(通常の出力を書き込むための)標準出力(standard output),(診断出力を書き込むための)標準エラー(standard error)とする。



この投稿にコメントする

削除パスワード

No.20159

Re:入力バッファに残ったデータを破棄したい
投稿者---chu-(2005/02/24 17:29:06)


>>ほんとに'\n'だけで判断して大丈夫なのかがすこし不安ですし、
>規格で行単位でバッファリングされる事が書いてありませんでしたっけ?

調べていただいてありがとうございます。
テキストストリームなのであれば'\n'とEOFで判断すれば間違いありませんね。

>>下記ソースのようにfscanfの場合に調べられないのでそれ以外を実験させていただきました。
>調べられない? 何が?

入力バッファにデータが残っているかどうかです。
RiSKさんの方法は、「>データがバッファに残っていること…」という条件を判断するのが難しいと思いました。
Blueさんの方法は、fgetsで読み込んだbufに'\n'がなければ入力バッファにデータが残っていると判断していますが、
fscanfでは判断材料がないと思いました。
それともfscanfはどんな書式指定をしても必ず入力バッファにデータが残っていると決めうちしても大丈夫なのでしょうか。

>>結果、rewind(stdin)とfseek(stdin, 0L, SEEK_SET)がうまくいきました。
>いずれも処理系依存(規格上未定義)。

ヘルプからはそう読めなかったので安心していたのですがダメでしたか。

ユーザが入力バッファにデータが残る入力をしても、残らない入力をしても、
どの入力関数であろうと、同じ一文で解決できる方法を考えていますが、
これには処理系依存に頼るしかないのでしょうか。


この投稿にコメントする

削除パスワード

No.20161

Re:入力バッファに残ったデータを破棄したい
投稿者---nop(2005/02/24 17:45:11)


>fscanfでは判断材料がないと思いました。
>それともfscanfはどんな書式指定をしても必ず入力バッファにデータが残っていると決めうちしても大丈夫なのでしょうか。

fscanf()は使用せず、fgets()+sscanf()で対応する。
scanf()、fscanf()にこだわる必要もないはずです。



この投稿にコメントする

削除パスワード

No.20162

Re:入力バッファに残ったデータを破棄したい
投稿者---RiSK(2005/02/25 10:34:04)


>入力バッファにデータが残っているかどうかです。
>RiSKさんの方法は、「>データがバッファに残っていること…」という条件を判断するのが難しいと思いました。

その判断はプログラマがうまくやらないとダメな気がします。

fgets なら '\n' まで読んだかどうかで判断。
fscanf(stdin, "%d %d", &a, &b); なら,2を返したときに
バッファにデータが残っているのは自明。
# 改行文字を食べる書式がないから。
# もしかしたら改行文字の手前にもデータがあるかもしれない。

>どの入力関数であろうと、同じ一文で解決できる方法を考えていますが、
>これには処理系依存に頼るしかないのでしょうか。

発想の逆転。よけいなデータを捨てる入力関数を作る!!


この投稿にコメントする

削除パスワード

No.20163

Re:入力バッファに残ったデータを破棄したい
投稿者---chu-(2005/02/25 18:26:14)


せっかく用意されている標準関数をなんとか活用できないかとこだわっていました。
しかし、環境によって入出力系が変わるのはしょうがないことでした。

皆さんの返信を参考に、以下のようになりました。
標準をそのまま使うより使い勝手がよくなり、
入力関数は用途ごとに自作して使った方が便利だと実感しています。

ありがとうございました。

[ソース]
#include <stdio.h>
#include <string.h>

int getint(int *a, int *b)
{
    char buf[32];
    char *p;
    int r;
    char c;
    fgets(buf, 32, stdin);
    r = sscanf(buf, "%d %d", a, b);
    if ( (p = strchr(buf, '\n')) == NULL ) {
        while ( (c = getchar()) != '\n' && c != EOF );
    }
    return r;
}

char *getstr(char *s, int n)
{
    char *p;
    char *r;
    char c;
    r = fgets(s, n, stdin);
    if ( (p = strchr(s, '\n')) != NULL ) {
        *p = '\0';
    }
    else {
        while ( (c = getchar()) != '\n' && c != EOF );
    }
    return r;
}

int main(void)
{
    int a, b, n;
    char s[4];
    char c[4];

    printf("a,b:");
    n = getint(&a, &b);
    printf("%d,%d - %d\n", a, b, n);

    printf("s[4]:");
    getstr(s, 4);
    printf("%02X,%02X,%02X,%02X\n", s[0], s[1], s[2], s[3]);

    printf("check");
    fgets(c, 4, stdin);
    printf("%02X,%02X,%02X,%02X\n", c[0], c[1], c[2], c[3]);

    return 0;
}

[実行結果]
a,b:9 8 7(Enter)
9,8 - 2
s[4]:123456(Enter)    ←" 7\x0A"が入り込んでいない
31,32,33,00
check(Enter)          ←"456\x0A"が入り込んでいない"
0A,00,A4,0F



この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧