关于在 Objective-C 中验证用户输入、数字与字符串的问题

发布于 2024-08-18 07:02:31 字数 880 浏览 6 评论 0原文

如果您输入非数字字符,为什么“完全”此代码会无限循环?

第一个问题的出现是因为我想学习良好的防御性编码。有谁知道检查用户输入的好方法?我的谷歌搜索失败了。有些人似乎认为,如果我在 scanf 中指定 %f ,我就“要求”一个 float;我通过打印 userInput 的值在某种程度上验证了这一点。事实上,如果我注释掉 do while 循环,代码的执行就“没有问题”。它将 0 分配给 userInput 并继续其业务。

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

枉心 2024-08-25 07:02:31

这实际上与 Objective-C 或 Cocoa 无关。该问题仅与标准 C 库函数 scanf 的使用以及错误情况的处理有关。来自 scanf 联机帮助页,描述返回代码:

零表示虽然有可用的输入,但未分配任何转换;通常,这是由于输入字符无效,例如用于“%d”转换的字母字符。

有效的数字输入可以由带有 %f 说明符的 scanf 进行解析,因此显然可以按预期工作。但如果您输入非数字字符,scanf 无法将其转换为浮点数,会将文本保留在 stdin 的缓冲区中。由于代码不检查 scanf 的返回码,并且仅测试 userInput 是否非零,因此循环永远不会退出,因为 userInput > 恰好从 0.0 开始,并且永远不会更新,因为 scanf 不会从 stdin 缓冲区中提取非数字字符。这就是为什么你的代码在无限循环中运行。

如果您将 userInput 初始化为非零值,则可以通过一种方式解决问题,因为非数字输入会导致 scanf 失败,并且 while 条件将被触发。但更好的解决方法是检查 scanf 的返回码。如果它为零,则打印一条错误消息,并在再次循环之前执行 fpurge(stdin) 来清除无效输入,如下所示:

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

所以这是输入和解析的纯 C 方法。防御性编码的底线是您应该始终检查返回代码!

正如 Chris 提到的,对于实际的 Cocoa 应用程序,您可能需要查看 NSNumberFormatter 等,但是您可能会从小部件而不是文件流中获取输入,因此代码将非常不同至上述。

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.

月依秋水 2024-08-25 07:02:31

在 Cocoa 中验证用户输入的正确方法是使用 NSFormatter 的适当子类的实例,在本例中类似于 NSNumberFormatter。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文