将由空格分隔的数字放入数组中

时间:2012-03-07 10:34:17

标签: c

我想让用户输入用空格分隔的数字,然后将每个值存储为数组的元素。目前我有:

while ((c = getchar()) != '\n')
{
    if (c != ' ')
        arr[i++] = c - '0'; 
}

但是,当然,每个元素存储一个数字。

如果用户要键入:

10 567 92 3

我希望将值10存储在arr[0]中,然后将{56}存储在arr[1]等中。

我应该以某种方式使用scanf吗?

3 个答案:

答案 0 :(得分:32)

有几种方法,具体取决于您希望代码的强大程度。

最直接的方法是将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分配给a[0]3分配给a[1],然后它会在输入流中看到r,返回0并退出没有保存任何内容a[2]。这里有一个微妙的bug进入 - 即使没有任何内容被分配给a[2],表达式i++仍然会被评估,所以你你已经为{{{ 1}}即使它包含垃圾值。因此,您可能希望暂时增加a[2],直到您知道自己已成功阅读:

i

理想情况下,您想完全拒绝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; } } 是一个空白字符 - 如果不是,我们会打印错误并退出循环。如果我们获得1次成功转换,则表示输入数字后面没有字符(意味着我们在数字输入后点击了EOF)。

或者,我们可以将每个输入值作为文本读取并使用follow进行转换,这也可以让您捕获相同类型的问题(我的首选方法):

strtol

但等等还有更多!

假设您的用户是那些喜欢在您的代码中输入令人讨厌的输入的扭曲QA类型之一“只是为了看看会发生什么”并输入#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; } } 这样的数字,显然 大到适合任何标准整数类型。信不信由你,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890不会对此嗤之以鼻,最终会将某些存储到scanf("%d", &val),但这又是一个你可能想要完全拒绝的输入。

如果每行只允许一个值,这就相对容易防范;如果有空间,val将在目标缓冲区中存储换行符,因此如果我们在输入缓冲区中没有看到换行符,那么用户输入的内容比我们准备处理的时间长:

fgets

如果你想允许每行多个值,例如'10 20 30',那么这会变得有点困难。我们可以回去从输入中读取单个字符,并对每个字符进行完整性检查(警告,未经测试):

#include <string.h>
...
while (i < ARRAY_SIZE && fgets(buf, sizeof 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, sizeof buf, stdin) != NULL)
      newline = strchr(buf, '\n');
    break;
  }
  ...
}

欢迎来到C中令人惊叹的交互式输入世界。

答案 1 :(得分:0)

对代码进行细微更改:只有在阅读空格时才会增加i

while ((c = getchar()) != '\n')
{
    if (c != ' ')
        arr[i] = arr[i] * 10 + c - '0'; 
    else
        i++;
}

当然,最好使用scanf

while (scanf("%d", &a[i++]) == 1);

假设您在阵列中有足够的空间。另外,请注意上面的while;结尾,一切都在循环条件内完成。

事实上,应该检查每个返回值。

答案 2 :(得分:0)

scanf返回成功扫描的项目数。 试试下面的代码:

#include <stdio.h>

int main()
{
  int arr[500];
  int i = 0;
  int sc = 0; //scanned items
  int n = 3; // no of integers to be scanned from the single line in stdin

   while( sc<n )
   {
      sc += scanf("%d",&arr[i++]);
   }
}