scanf is not waiting for input within my loop?

2019-08-09 10:39发布

问题:

I'm new to Objective-C, and this is really my first program that is interactive. I've been learning for about 2 weeks now.

So, my question is: typically I've noticed when you have multiple scanf's in a row, they each wait for input - however in this situation, where I ask for account owner name, and balance - it fires both NSLog functions instead of waiting for the first input.

Here is my main:

int main(int argc, char* argV[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    bank *columbiaBank = [[bank alloc] init];

    int iteration = 0;
    while (true) {
        int selection = 0;
        NSLog(@"\n1. Add Account \n2. Remove Account \n3. Modify Account \nWhat would you like to do?:");
        scanf("%i", &selection);

        if (selection == 1) {

            NSLog(@"\nEnter account owner:");
            char accountOwner;
            scanf("%c", &accountOwner);

            NSLog(@"\nEnter opening balance:");
            float openingBalance;
            scanf("%f", &openingBalance);

            // create and add new account
            bankAccount *newAccount = [[bankAccount alloc] initWithProps:[NSString stringWithFormat:@"%c", accountOwner] :[NSString stringWithFormat:@"%i", iteration] :openingBalance];
            [columbiaBank addAccount:newAccount];
            [newAccount release];
            NSLog(@"\nAccount successfully added!");

       } else if (selection == 2) {

            NSLog(@"\nEnter account id:");
            int accountId;
            scanf("%i", &accountId);
            // remove account
            [columbiaBank removeAccount:[NSString stringWithFormat:@"%i", accountId]];
            NSLog(@"\nAccount successfully removed!");

        } else if (selection == 3) {

            NSLog(@"\nThe bank currently has %i accounts.", columbiaBank.totalAccounts);
            NSLog(@"\nThe bank's current balance from all accounts is $%f", columbiaBank.totalBankBalance);
            NSLog(@"\n-- Output of all account info --");
            [columbiaBank printAccounts];

        } else {

            NSLog(@"You did not enter a valid action.");

        }
        iteration++;
    }

    [columbiaBank release];
    [pool drain];
    return false;
}

回答1:

*[Note: If you are going to use Objective-C you might wish to use input conversion methods from Cocoa rather than mix Cocoa (NSLog) and stdio (scanf). But that doesn't answer your question...]

When parsing integers, floats and even strings scanf skips whitespace - e.g. spaces, tabs, end of line, etc. - and every input line ends with at least an end of line (which may be a carriage return, line feed, or both depending on the system). This means that after reading your first integer there is still, at least, an end of line in the input and the attempt to read a character will return it - hence no wait for input. To discard the remaining, unused, input you can use fpurge. E.g.:

#include <stdio.h>

int main(int argc, char* argV[])
{
    int selection = 0;
    fputs("\n1. Add Account \n2. Remove Account \n3. Modify Account \nWhat would you like to do?: ", stdout);
    scanf("%i", &selection);

    if (selection == 1)
    {

        fputs("\nEnter account owner: ", stdout);
        fpurge(stdin); // skip any input left in the buffer as %c takes the very next character and does not skip whitespace
        char accountOwner;
        scanf("%c", &accountOwner);

        fputs("\nEnter opening balance: ", stdout);
        float openingBalance;
        scanf("%f", &openingBalance);

        printf("%c - %f\n", accountOwner, openingBalance);
    }
}

Note that reading in character strings does skip whitespace, so if your account owner was a string you would not need the fpurge.



回答2:

Other users already said everything about it. The scanf inserts a new line "\n" automatically in the buffer that is passed to the next scanf. This is because any unwritten data is written in the next stream.

I want to add that you can use fflush to clear the stream buffer, in this case you want to use

scanf("%i", &selection);
fflush(stdin)

to clear the buffer of stdin (the console input) after every scanf.

Edit: I didn't know that, but As @Peter Kowalski said the use of fflush(stdin), for input stream, should be avoided because it has an undefined behaviour for input streams.

Cprograming.com FAQ > Why fflush(stdin) is wrong.

But it seems that there is not a guaranteed method to flush the input stream in C.

Cprograming.com FAQ > Flush the input stream

I know that in C++ a standard way is to use cin.ignore() after cin >> selection but I don't know how this can be done in C. Maybe some more experienced user can give some insight on what is happening with fflush(stdin).



回答3:

Presumably you want the account owner name to be more than a single character, but you're only reading a single character in that scanf. If you're trying to enter more than a single character there, the first scanf will read the first character, and since there's more in the input buffer, the next scanf will try to read immediately without waiting for your numeric input. If you are only using a single character for the owner name, then you'll need to consume the newline from the input buffer.

If you want to read a string as the account owner name, you'll need to allocate space for more than one character, and use %s rather than %c as your scanf format string. Also remember to check the return value for scanf. The function will return the number of items successfully scanned, or 0 if no items were scanned, typically due to invalid input, or return EOF.

char accountOwner[26];

// ...

// note that you can specify a width (max length) for a string using scanf
scanfReturn = scanf("%25s", accountOwner);