关于在 Objective-C 中验证用户输入、数字与字符串的问题
如果您输入非数字字符,为什么“完全”此代码会无限循环?
第一个问题的出现是因为我想学习良好的防御性编码。有谁知道检查用户输入的好方法?我的谷歌搜索失败了。有些人似乎认为,如果我在 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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这实际上与 Objective-C 或 Cocoa 无关。该问题仅与标准 C 库函数
scanf
的使用以及错误情况的处理有关。来自scanf
联机帮助页,描述返回代码:有效的数字输入可以由带有
%f
说明符的scanf
进行解析,因此显然可以按预期工作。但如果您输入非数字字符,scanf
无法将其转换为浮点数,会将文本保留在stdin
的缓冲区中。由于代码不检查scanf
的返回码,并且仅测试userInput
是否非零,因此循环永远不会退出,因为userInput
> 恰好从0.0
开始,并且永远不会更新,因为scanf
不会从stdin
缓冲区中提取非数字字符。这就是为什么你的代码在无限循环中运行。如果您将
userInput
初始化为非零值,则可以通过一种方式解决问题,因为非数字输入会导致scanf
失败,并且while
条件将被触发。但更好的解决方法是检查scanf
的返回码。如果它为零,则打印一条错误消息,并在再次循环之前执行 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 thescanf
manpage, describing the return code: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 ofstdin
. Since the code is not checking the return code fromscanf
, and only testing ifuserInput
is non-zero, the loop will never exit, asuserInput
happens to start at0.0
, and will never be updated asscanf
will not pull the non-numeric characters out of thestdin
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 causescanf
to fail and thewhile
condition would be triggered. But a better fix would be to check the return code ofscanf
. If it is zero, print an error message, and do afpurge(stdin)
to clear out the invalid input before you loop around again, like this: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.在 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.