Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Putting numbers separated by a space into an array

Tags:

c

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.

If the user was to type:

10 567 92 3

I was wanting the value 10 to be stored in arr[0], and then 567 in arr[1] etc.

Should I be using scanf instead somehow?

like image 712
bqui56 Avatar asked Mar 07 '12 10:03

bqui56


People also ask

How do you input space separated integers in array?

How can I store these 'n' integers in an array. Use a simple for loop. int i, n, arr[100]; scanf("%d", &n); for (i = 0; i < n; ++i) scanf("%d", &arr[i]);

Can you split a number into an array?

To split a number into an array:Use the Array. from() method to convert the string into an array of digits. Use the map() function to convert each string in the array to a number.

How do you take space separated input into an array in Python?

Use an input() function to accept the list elements from a user in the format of a string separated by space. Next, use a split() function to split an input string by space. The split() method splits a string into a list.

How do you print an array with spaces?

for(i=0;i<n;i++) printf("%c ",arr[i]); Notice a whitespace i.e ' ' is written after %c . This ensures a whitespace after every character from array is printed(whitespace will also be there after the last element).


1 Answers

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

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

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

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

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 */;

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++;

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

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

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

BUT WAIT THERE'S MORE!

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.

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

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

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

like image 176
John Bode Avatar answered Sep 21 '22 05:09

John Bode