开发者

Questions about validating user input in Objective-C, number vs string

开发者 https://www.devze.com 2022-12-16 20:20 出处:网络
Why \'exactly\' does this code loop endlessly if you enter a non number character? The first question comes about because I want to learn good defensive coding. Does anyone know a good way to check u

Why 'exactly' does this code loop endlessly if you enter a non number character?

The first question comes about because I want to learn good defensive coding. Does anyone know a good way to check user input? My google-fu failed me. Some people seemed to be of the opinion that if I specify %f in scanf that I am 'demanding' a float; I verified this, in a way, by printing the value of userInput. In fact, if I comment out the do while loop, there is 'no problem' with the execution of the code. It assigns a 0 to userInput and goes about its business.

#import <Foundation/Foundation.h>

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

    float userInput;
    float 开发者_高级运维result;

    NSLog(@"3X^3 -5x^2 + 6");

    do {
        NSLog(@"What is x?");
        scanf("%f", &userInput);
        NSLog(@"userInput = %f", userInput);
    } while(userInput == 0);

    result = 3 * (userInput * userInput * userInput) - 5 * (userInput * userInput) + 6;
    NSLog(@"the result is: %f", result);

    [pool drain];
    return 0;
}


This is really nothing to do with Objective-C or Cocoa. The issue is simply to do with the use of the standard C library function scanf, and handling the error condition. From the scanf manpage, describing the return code:

Zero indicates that, although there was input available, no conversions were assigned; typically this is due to an invalid input character, such as an alphabetic character for a `%d' conversion.

A valid numeric input can be parsed by scanf with the %f specifier, so that obviously works as expected. But if you enter in a non-numeric character, scanf cannot convert this to a float, and leaves the text in the buffer of stdin. Since the code is not checking the return code from scanf, and only testing if userInput is non-zero, the loop will never exit, as userInput happens to start at 0.0, and will never be updated as scanf will not pull the non-numeric characters out of the stdin buffer. So that is why your code runs in an infinite loop.

If you had initialized userInput to a non-zero value, that would fix the problem one way, as non-numeric input would cause scanf to fail and the while condition would be triggered. But a better fix would be to check the return code of scanf. If it is zero, print an error message, and do a fpurge(stdin) to clear out the invalid input before you loop around again, like this:

int rc = scanf("%f", &userInput);
if (rc == 0)
{
    NSLog(@"Invalid input, try again.");
    fpurge(stdin);
}

So this is the plain C approach to input and parsing. The bottom line for defensive coding is that you should always check the return code!

As Chris mentions, for an actual Cocoa application, you would want to look at NSNumberFormatter and the like, but then you would presumably be taking input from widgets rather than file streams, so the code would be quite different to the above.


The proper way to validate user input in Cocoa is to use an instance of an appropriate subclass of NSFormatter, in this case something like NSNumberFormatter.

0

精彩评论

暂无评论...
验证码 换一张
取 消