把用空格分隔的数字到一个数组 [英] Putting numbers separated by a space into an array

查看:142
本文介绍了把用空格分隔的数字到一个数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to have a user enter numbers separated by a space and then store each value as an element of an array. Currently I have:

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

不过,当然,这个存储一个每个元素的数字。

but, of course, this stores one digit per element.

如果用户是键入:

10 567 92 3

我想10存储在改编[0] ,然后567 改编[1] 等。

我应该使用 scanf函数而不是以某种方式?

Should I be using scanf instead somehow?

推荐答案

有几种方法,这取决于您希望如何健壮的code是。

There are several approaches, depending on how robust you want the code to be.

最简单的就是使用 scanf函数%d个转换说明:

The most straightforward is to use scanf with the %d conversion specifier:

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

%d个转换符告诉 scanf函数来跳过任何前导空格和读取到下一个非数字字符。返回值是成功转换和分配的数量。由于我们正在阅读一个整数值,则返回值应该是1成功。

The %d conversion specifier tells scanf to skip over any leading whitespace and read up to the next non-digit character. The return value is the number of successful conversions and assignments. Since we're reading a single integer value, the return value should be 1 on success.

由于写的,这有一些陷阱。首先,假设你的用户输入更多的数字比你的数组的大小持有;如果你幸运的话,你会立即收到一条访问冲突。如果你没有,你会风重挫重要的事以后会引起问题(缓冲区溢出是一种常见的恶意软件攻击)。

As written, this has a number of pitfalls. First, suppose your user enters more numbers than your array is sized to hold; if you're lucky you'll get an access violation immediately. If you're not, you'll wind up clobbering something important that will cause problems later (buffer overflows are a common malware exploit).

所以,你至少要添加code,以确保你不走过去的数组的末尾:

So you at least want to add code to make sure you don't go past the end of your array:

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

好为止。但现在假设您的用户fatfingers一个非数字字符在他们的输入,如 12 3r5 67 。按照规定,循环将分配 12 A [0] 3 A [1] ,那么将看到研究输入流中,返回0,不保存任何退出A [2] 。这里就是一个微妙的错误毛骨悚然的 - 即使没有被分配到 A [2] ,前pression 我++ 仍然得到评估,因此,您的认为的您分配东西<​​code> A [2] 即使它包含一个垃圾值。所以,你可能要推迟递增 I ,直到你知道你有一个成功读取:

Good so far. But now suppose your user fatfingers a non-numeric character in their input, like 12 3r5 67. As written, the loop will assign 12 to a[0], 3 to a[1], then it will see the r in the input stream, return 0 and exit without saving anything to a[2]. Here's where a subtle bug creeps in -- even though nothing gets assigned to a[2], the expression i++ still gets evaluated, so you'll think you assigned something to a[2] even though it contains a garbage value. So you might want to hold off on incrementing i until you know you had a successful read:

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

在理想情况下,你想拒绝 3r5 干脆。我们可以读出的字符立即数以下,并确保它是空白;如果它不是,我们拒绝输入:

Ideally, you'd like to reject 3r5 altogether. We can read the character immediately following the number and make sure it's whitespace; if it's not, we reject the input:

#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)。

If we get two successful conversions, we make sure follow is a whitespace character - if it isn't, we print an error and exit the loop. If we get 1 successful conversion, that means there were no characters following the input number (meaning we hit EOF after the numeric input).

另外,我们可以看到每个输入值的文本,并使用与strtol 来进行转换,这也可以让你赶上同一类问题(我的preferred法):

Alternately, we can read each input value as text and use strtol to do the conversion, which also allows you to catch the same kind of problem (my preferred method):

#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;
  }
}

别急,还有更多!

假设你的用户是谁喜欢在你的code只是为了看看会发生什么,进入一个像扔厌恶那些输入扭曲QA类型之一<$c$c>123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890这是的显然的过大,以适应任何标准的整数类型。信不信由你, scanf函数(%d个,&放大器; VAL)不会牦牛这一点,并且将结束存储的的东西 VAL ,但同样是你可能想直接拒绝输入。

Suppose your user is one of those twisted QA types who likes to throw obnoxious input at your code "just to see what happens" and enters a number like 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 which is obviously too large to fit into any of the standard integer types. Believe it or not, scanf("%d", &val) will not yak on this, and will wind up storing something to val, but again it's an input you'd probably like to reject outright.

如果你只允许每行一个值,这成为比较容易防范; 与fgets 将换行符存储到目标缓冲区,如果有足够的空间,因此,如果我们看不到输入缓冲区换行符,那么用户输入的东西是超过我们再prepared处理:

If you only allow one value per line, this becomes relatively easy to guard against; fgets will store a newline character in the target buffer if there's room, so if we don't see a newline character in the input buffer then the user typed something that's longer than we're prepared to handle:

#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;
  }
  ...
}

如果你想允许每行多个值,如'10 20 30',那么这个有点难度。我们可以回到从输入读取单个字符,并在每一个(警告,未经测试)做一个全面的检查:

If you want to allow multiple values per line such as '10 20 30', then this gets a bit harder. We could go back to reading individual characters from the input, and doing a sanity check on each (warning, untested):

...
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.

Welcome to the wonderfully whacked-up world of interactive input in C.

这篇关于把用空格分隔的数字到一个数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆