while ((c = getchar()) != '\n') { if (c != ' ') arr[i++] = c - '0'; }
但是,当然,这存储每个元素一个数字.
如果用户要输入:
10 567 92 3
我想要将值10存储在arr [0]中,然后在arr [1]等中存储567.
我应该使用scanf吗?
解决方法
最直接的是使用scanf与%d转换说明符:
while (scanf("%d",&a[i++]) == 1) /* empty loop */ ;
%d转换说明符告诉scanf跳过任何前导的空格并读取到下一个非数字字符.返回值是转换和分配成功的次数.由于我们正在读取一个整数值,所以成功返回值应为1.
正如书面的,这有一些陷阱.首先,假设您的用户输入的数字大于数组的大小,以保持;如果你很幸运,你会立即获得访问冲突.如果不这样做,那么你会发现一些重要的东西会在以后引起问题(缓冲区溢出是常见的恶意软件漏洞).
while (i < ARRAY_SIZE && scanf("%d",&a[i++]) == 1) /* empty loop */;
目前很好.但现在假设您的用户在其输入中输入非数字字符,例如12 3r5 67.如所写,循环将分配12到[0],3到a [1],然后它将在输入中看到r流,返回0并退出而不保存任何东西到[2].这里是一个微妙的bug蠕虫 – 即使没有任何东西被分配到一个[2],表达式我仍然被评估,所以你会认为你分配了一个[2],即使它包含垃圾值.所以你可能希望在增加我的时候,直到你知道你读了一个成功:
while (i < ARRAY_SIZE && scanf("%d",&a[i]) == 1) i++;
理想情况下,您想完全拒绝3r5.我们可以在数字后面仔细阅读字符,并确保它是空格;如果不是,我们拒绝输入:
#include <ctype.h> ... int tmp; char follow; int count; ... while (i < ARRAY_SIZE && (count = scanf("%d%c",&tmp,&follow)) > 0) { if (count == 2 && isspace(follow) || count == 1) { a[i++] = tmp; } else { printf ("Bad character detected: %c\n",follow); break; } }
如果我们得到两个成功的转换,我们确保遵循一个空白字符 – 如果不是,我们打印错误并退出循环.如果我们得到一个成功的转换,那意味着输入号后没有字符(意味着我们在数字输入后打到EOF).
或者,我们可以读取每个输入值作为文本,并使用strtol进行转换,这也允许您捕获同样的问题(我的首选方法):
#include <ctype.h> #include <stdlib.h> ... char buf[INT_DIGITS + 3]; // account for sign character,newline,and 0 terminator ... while(i < ARRAY_SIZE && fgets(buf,sizeof buf,stdin) != NULL) { char *follow; // note that follow is a pointer to char in this case int val = (int) strtol(buf,&follow,10); if (isspace(*follow) || *follow == 0) { a[i++] = val; } else { printf("%s is not a valid integer string; exiting...\n",buf); break; } }
但等待更多!
假设你的用户是谁喜欢在代码中引发厌恶那些输入扭曲QA类型之一“只是为了看看会发生什么”,并进入了一些像123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890这显然是太大,以适应任何标准的整数类型.相信与否,scanf(“%d”,&val)将不会这样做,并且会将某些东西储存起来,但再次是您可能希望拒绝的输入.
如果你每行只允许一个值,这样就可以很容易地防范;如果有空格,则fgets将在目标缓冲区中存储一个换行字符,所以如果输入缓冲区中没有看到换行符,那么用户键入的内容比我们准备处理的更长一些:
#include <string.h> ... while (i < ARRAY_SIZE && fgets(buf,stdin) != NULL) { char *newline = strchr(buf,'\n'); if (!newline) { printf("Input value too long\n"); /** * Read until we see a newline or EOF to clear out the input stream */ while (!newline && fgets(buf,stdin) != NULL) newline = strchr(buf,'\n'); break; } ... }
如果要允许每行多个值,例如’10 20 30′,那么这会变得更困难.我们可以返回从输入中读取单个角色,并对每个角色进行理智检查(警告,未经测试):
... while (i < ARRAY_SIZE) { size_t j = 0; int c; while (j < sizeof buf - 1 && (c = getchar()) != EOF) && isdigit(c)) buf[j++] = c; buf[j] = 0; if (isdigit(c)) { printf("Input too long to handle\n"); while ((c = getchar()) != EOF && c != '\n') // clear out input stream /* empty loop */ ; break; } else if (!isspace(c)) { if (isgraph(c) printf("Non-digit character %c seen in numeric input\n",c); else printf("Non-digit character %o seen in numeric input\n",c); while ((c = getchar()) != EOF && c != '\n') // clear out input stream /* empty loop */ break; } else a[i++] = (int) strtol(buffer,NULL,10); // no need for follow pointer,// since we've already checked // for non-digit characters. }
欢迎来到C的奇妙的交互式输入世界