![](/img/trans.png)
[英]How do i know if the input is an character or a float number with scanf?
[英]if my scanf variable is a float and a user inputs a character how can i prompt them to input a number? assuming the scanf is inside a do while loop
我曾尝试使用 k = getchar() 但它也不起作用;
这是我的代码
#include<stdio.h>
int main()
{
float height;
float k=0;
do
{
printf("please type a value..\n");
scanf("%f",&height);
k=height;
}while(k<0);// i assume letters and non positive numbers are below zero.
//so i want the loop to continue until one types a +ve float.
printf("%f",k);
return 0;
}
我想要一个如果用户键入字母或负数或字符,他/她应该被提示再次键入该值,直到他键入一个正数
我一般不会使用scanf
-family 函数从stdin
读取。
fgets
更好,因为它将输入作为您指定长度的字符串,避免缓冲区溢出,您可以稍后将其解析为所需的类型(如果有)。 对于float
值, strtof
有效。
但是,如果您的可交付成果或家庭作业的规范要求使用scanf
和%f
作为格式说明符,您可以做的是检查其返回值,该值将包含格式字符串中格式说明符的数量的计数已成功扫描:
第 7.21.6.2 节:
如果在第一次转换(如果有)完成之前发生输入失败,[scanf] 函数将返回宏 EOF 的值。 否则,该函数返回分配的输入项的数量,如果早期匹配失败,该数量可能少于提供的数量,甚至为零。
从那里,您可以诊断输入是否有效。 此外,当scanf
失败时, stdin
不会被清除,随后对scanf
调用(即在循环中)将继续查看其中的内容。 这个问题有一些关于处理这个问题的信息。
就像 Govind Parmar已经建议的那样,使用fgets()
读取整行输入更好/更容易,而不是使用scanf()
等。 用于人机交互输入。
根本原因是交互式标准输入在默认情况下是行缓冲的(并且更改它很重要)。 因此,当用户开始输入他们的输入时,它不会立即提供给您的程序; 仅当用户按下Enter 时。
如果我们确实使用fgets()
读取输入的每一行,那么我们可以使用sscanf()
对其进行扫描和转换,其工作方式与scanf()
/ fscanf()
非常相似,只是sscanf()
对字符串输入起作用,而不是一个输入流。
这是一个实际的例子:
#include <stdlib.h>
#include <stdio.h>
#define MAX_LINE_LEN 100
int main(void)
{
char buffer[MAX_LINE_LEN + 1];
char *line, dummy;
double value;
while (1) {
printf("Please type a number, or Q to exit:\n");
fflush(stdout);
line = fgets(buffer, sizeof buffer, stdin);
if (!line) {
printf("No more input; exiting.\n");
break;
}
if (sscanf(line, " %lf %c", &value, &dummy) == 1) {
printf("You typed %.6f\n", value);
continue;
}
if (line[0] == 'q' || line[0] == 'Q') {
printf("Thank you; now quitting.\n");
break;
}
printf("Sorry, I couldn't parse that.\n");
}
return EXIT_SUCCESS;
}
fflush(stdout);
没有必要,但也没有坏处。 它基本上确保我们所有printf()
d 或写入stdout
都将刷新到文件或设备; 在这种情况下,它将显示在终端中。 (这里没有必要,因为标准输出默认也是行缓冲的,所以printf模式中的\\n
,打印换行符,也会引起flush。
我确实喜欢将那些fflush()
调用洒在我需要记住的任何地方,此时所有输出实际上都被刷新到输出,而不是被 C 库缓存,这一点很重要。 在这种情况下,我们肯定希望在开始等待用户输入之前提示对用户可见!
(但是,同样,因为printf("...\\n");
在它以换行符结束之前, \\n
,并且我们没有更改标准输出缓冲,因此不需要fflush(stdout);
。 )
该line = fgets(buffer, sizeof buffer, stdin);
行包含几个重要的细节:
我们之前定义了宏MAX_LINE_LEN
,因为fgets()
只能读取给定缓冲区的行,并且将在后续调用中返回该行的其余部分。
(您可以检查读取的行是否以换行符结尾:如果没有,则它是输入文件中的最后一行,在最后一行的末尾没有换行符,或者该行比您拥有的缓冲区,因此您只收到了初始部分,其余部分仍在缓冲区中等待您。)
char buffer[MAX_LINE_LEN + 1];
的+1
char buffer[MAX_LINE_LEN + 1];
是因为 C 中的字符串以空字符'\\0'
结尾。 所以,如果我们有一个 19 个字符的缓冲区,它最多可以保存一个 18 个字符的字符串。
请注意,NUL 或带一个 ell 的 nul 是代码为 0 的 ASCII 字符的名称,即'\\0'
,并且是字符串结束标记字符。
然而,NULL(或有时为 nil)是指向零地址的指针,在 C99 及更高版本中与(void *)0
。 它是我们使用的哨兵和错误值,当我们想设置一个指向可识别错误/未使用/无值的指针,而不是指向实际数据时。
sizeof buffer
是变量buffer
使用的字符总数(包括字符串结尾的 nul 字符)。
在这种情况下,我们可以使用MAX_LINE_LEN + 1
来代替( fgets()
的第二个参数是给定缓冲区中的字符数,包括对字符串结尾字符的保留)。
我在这里使用sizeof buffer
的原因是因为它非常有用。 (请记住,如果buffer
是指针而不是数组,它将计算指针的大小;而不是该指针指向的可用数据量。如果使用指针,则需要跟踪内存量在那里你自己可用,通常在一个单独的变量中。这就是 C 的工作方式。)
也因为sizeof
不是函数而是运算符很重要:它不评估其参数,它只考虑参数的大小(类型)。 这意味着如果你做一些像sizeof (i++)
这样愚蠢的事情,你会发现i
没有增加,并且它产生与sizeof i
完全相同的值。 同样,这是因为sizeof
是一个运算符,而不是一个函数,它只返回其参数的大小。
fgets()
返回指向它存储在缓冲区中的行的指针,如果发生错误,则返回NULL
。
这也是我命名指针line
和存储数组buffer
。 他们描述了我作为程序员的意图。 (顺便说一句,这在编写注释时非常重要:不要描述代码的作用,因为我们可以阅读代码;但要描述你对代码应该做什么的意图,因为只有程序员知道,但它如果人们试图理解、修改或修复代码,那么了解该意图很重要。)
scanf() 系列函数返回成功转换的次数。 为了检测正确数值后跟垃圾的输入,比如1.0 x
,我要求sscanf()
忽略数字后面的任何空格(空格表示制表符、空格和换行符; '\\t'
、 '\\n'
、 '\\v'
、 '\\f'
、 '\\r'
和' '
用于使用 ASCII 字符集的默认 C 语言环境),并尝试转换单个附加字符dummy
。
现在,如果该行确实包含除数字后面的空格之外的任何内容,则sscanf()
将在dummy
存储该内容的第一个字符,并返回 2。但是,因为我只想要只包含数字而不包含虚拟字符的行,我期望返回值为 1。
要检测q
或Q
(但仅作为该行的第一个字符),我们只需检查line
的第一个字符line[0]
。
如果我们包含<string.h>
,我们可以使用例如if (strchr(line, 'q') || strchr(line, 'Q'))
来查看所提供的行中是否有q
或Q
。 strchr(string, char)
返回指向字符串中第一次出现的 char 的指针,如果没有则返回 NULL; 并且除 NULL 之外的所有指针在逻辑上都被认为是正确的。 (也就是说,我们可以等效地写if (strchr(line, 'q') != NULL || strchr(line, 'Q') != NULL)
。)
我们可以在<string.h>
声明的另一个函数是strstr()
。 它的工作原理类似于strchr()
,但第二个参数是一个字符串。 例如, (strstr(line, "exit"))
仅当line
在某处有exit
时才为真。 (不过,它可能是brexit
或exitology
;这只是一个简单的子字符串搜索。)
在循环中, continue
跳过循环体的其余部分,并从头开始循环体的下一次迭代。
在循环中, break
跳过循环体的其余部分,并在循环后继续执行。
EXIT_SUCCESS
和EXIT_FAILURE
是<stdlib.h>
定义的标准退出状态代码。 大多数人更喜欢将0
用于 EXIT_SUCCESS(因为在大多数操作系统中都是这样),但我认为像这样拼写成功/失败可以更容易阅读代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.