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

bqu*_*i56 13 c

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

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

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

如果用户要输入:

10 567 92 3
Run Code Online (Sandbox Code Playgroud)

我想要存储值10 arr[0],然后567 arr[1]等.

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

Joh*_*ode 32

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

最直接的方法是使用scanf%d转换符:

while (scanf("%d", &a[i++]) == 1)
  /* empty loop */ ;
Run Code Online (Sandbox Code Playgroud)

%d转换符告诉scanf跳过任何前导空格,并宣读了下一非数字字符.返回值是成功转换和分配的数量.由于我们正在读取单个整数值,因此成功时返回值应为1.

如上所述,这有许多陷阱.首先,假设您的用户输入的数字多于阵列的大小以容纳; 如果你很幸运,你会立即获得访问冲突.如果你不是,你最终会破坏一些重要的东西,以后会引起问题(缓冲区溢出是一种常见的恶意软件攻击).

因此,您至少要添加代码以确保不会超过数组的末尾:

while (i < ARRAY_SIZE && scanf("%d", &a[i++]) == 1)
  /* empty loop */;
Run Code Online (Sandbox Code Playgroud)

目前很好.但是现在假设你的用户在输入中使用非数字字符,比如12 3r5 67.按照规定,环将分配12a[0],3a[1],那么它会看到r在输入流中,但不保存任何东西,返回0并退出a[2].这是一个微妙的bug进入的地方 - 即使没有任何内容被分配a[2],表达式i++仍然会被评估,所以你会认为你已经分配了一些内容,a[2]即使它包含一个垃圾值.所以你可能想要继续增加,i直到你知道你有一个成功的阅读:

while (i < ARRAY_SIZE && scanf("%d", &a[i]) == 1)
  i++;
Run Code Online (Sandbox Code Playgroud)

理想情况下,你想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;
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我们得到两次成功的转换,我们确保follow是一个空格字符 - 如果不是,我们打印错误并退出循环.如果我们获得1次成功转换,则表示输入数字后面没有字符(意味着我们在数字输入后点击了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;
  }
}
Run Code Online (Sandbox Code Playgroud)

但等等还有更多!

假设你的用户是谁喜欢在代码中引发厌恶那些输入扭曲QA类型之一"只是为了看看会发生什么",并进入了一些像123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890显然过大,以适应任何标准的整数类型.信不信由你,scanf("%d", &val)不会牦牛这一点,并会倒闭存储的东西val,但同样是你可能想直接拒绝输入.

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

#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;
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

如果你想允许每行多个值,例如'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.
}
Run Code Online (Sandbox Code Playgroud)

欢迎来到C中令人惊叹的互动输入世界.