Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
301 views
in Technique[技术] by (71.8m points)

c - Flushing stdin after every input - which approach is not buggy?

After Mark Lakata pointed out that the garbage isn't properly defined in my question I came up with this. I'll keep this updated to avoid confusions.


I am trying to get a function that I can call before a prompt for user input like printf("Enter your choice:); followed a scanf and be sure that only the things entered after the prompt would be scanned in by scanf as valid input.

As far as I can understand the function that is needed is something that flushes standard input completely. That is what I want. So for the purpose of this function the "garbage" is everything in user input i.e. the whole user input before that user prompt.


While using scanf() in C there is always the problem of extra input lying in the input buffer. So I was looking for a function that I call after every scanf call to remedy this problem. I used this, this, this and this to get these answers

//First approach
scanf("%*[^
]
");

//2ndapproach
scanf("%*[^
]%*c");

//3rd approach
int c;
while((c = getchar()) != EOF) 
    if (c == '
') 
        break;

All three are working as far as I could find by hit-and-trial and going by the references. But before using any of these in all of my codes I wanted to know whether any of these have any bugs?

EDIT:

Thanks to Mark Lakata for one bug in 3rd. I corrected it in the question.

EDIT2:

After Jerry Coffin answered I tested the 1st 2 approaches using this program in code:blocks IDE 12.11 using GNU GCC Compiler(Version not stated in the compiler settings).

#include<stdio.h>

int main()
{
    int x = 3; //Some arbitrary value
    //1st one
    scanf("%*[^
]
");
    scanf("%d", &x);
    printf("%d
", x);

    x = 3;
    //2nd one
    scanf("%*[^
]%*c");
    scanf("%d", &x);
    printf("%d", x);
}

I used the following 2 inputs

First Test Input (2 Newlines but no spaces in the middle of garbage input)

abhabdjasxd


23
bbhvdahdbkajdnalkalkd



46

For the first I got the following output by the printf statements

23
46

i.e. both codes worked properly.

Second Test input: (2 Newlines with spaces in the middle of garbage input)

hahasjbas asasadlk


23
manbdjas sadjadja a


46

For the second I got the following output by the printf statements

23
3

Hence I found that the second one won't be taking care of extra garbage input whitespaces. Hence, it isn't foolproof against garbage input.

I decided to try out a 3rd test case (garbage includes newline before and after the non-whitespace character)

``
hahasjbas asasadlk


23

manbdjas sadjadja a


46

The answer was

3
3

i.e. both failed in this test case.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The first two are subtly different: they both read and ignore all the characters up to a new-line. Then the first skips all consecutive white space so after it executes, the next character you read will be non-whitespace.

The second reads and ignores characters until it encounters a new-line then reads (and discards) exactly one more character.

The difference will show up if you have (for example) double-spaced text, like:

line 1

line 2

Let's assume you read to somewhere in the middle of line 1. If you then execute the first one, the next character you read in will be the 'l' on line 2. If you execute the second, the next character you read in will be the new-line between line 1 and line 2.

As for the third, if I were going to do this at all, I'd do something like:

int ch;
while ((ch=getchar()) != EOF && ch != '
')
    ;

...and yes, this does work correctly -- && forces a sequence point, so its left operand is evaluated first. Then there's a sequence point. Then, if and only if the left operand evaluated to true, it evaluates its right operand.

As for performance differences: since you're dealing with I/O to start with, there's little reasonable question that all of these will always be I/O bound. Despite its apparent complexity, scanf (and company) are usually code that's been used and carefully optimized over years of use. In this case, the hand-rolled loop may be quite a bit slower (e.g., if the code for getchar doesn't get expanded inline) or it may be about the same speed. The only way it stands any chance of being significantly faster is if the person who wrote your standard library was incompetent.

As far maintainability: IMO, anybody who claims to know C should know the scan set conversion for scanf. This is neither new nor rocket science. Anybody who doesn't know it really isn't a competent C programmer.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...