2

I wanted to know if there was a way to use scanf so I can take in an unknown number of string arguments and put them into a char* array. I have seen it being done with int values, but can't find a way for it to be done with char arrays. Also the arguments are entered on the same line separated by spaces.

Example: user enters hello goodbye yes, hello gets stored in array[0], goodbye in array[1] and yes in array[2]. Or the user could just enter hello and then the only thing in the array would be hello.

I do not really have any code to post, as I have no real idea how to do this.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278

3 Answers3

3

You can do something like, read until the "\n" :

scanf("%[^\n]",buffer);

you need to allocate before hand a big enough buffer.

Now go through the buffer count the number of words, and allocate the necessary space char **array = ....(dynamic string allocation), go to the buffer and copy string by string into the array.

An example:

int words = 1;
char buffer[128];
int result = scanf("%127[^\n]",buffer);

if(result > 0)
{
    char **array;

    for(int i = 0; buffer[i]!='\0'; i++)
    {
       if(buffer[i]==' ' || buffer[i]=='\n' || buffer[i]=='\t')
       {
        words++;
       }
    }

   array = malloc(words * sizeof(char*));

   // Using RoadRunner suggestion
  array[0] = strtok (buffer," ");
  for(int w = 1; w < words; w++)
  {
     array[w] = strtok (NULL," ");
  }
}

As mention in the comments you should use (if you can) fgets instead fgets(buffer,128,stdin);. More about strtok

Community
  • 1
  • 1
dreamcrash
  • 47,137
  • 25
  • 94
  • 117
2

If you have an upper bound to the number of strings you may receive from the user, and to the number of characters in each string, and all strings are entered on a single line, you can do this with the following steps:

  • read the full line with fgets(),
  • parse the line with sscanf() with a format string with the maximum number of %s conversion specifiers.

Here is an example for up to 10 strings, each up to 32 characters:

    char buf[400];
    char s[10][32 + 1];
    int n = 0;

    if (fgets(buf, sizeof buf, sdtin)) {
        n = sscanf("%32s%32s%32s%32s%32s%32s%32s%32s%32s%32s",
                   s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9]));
    }
    // `n` contains the number of strings
    // s[0], s[1]... contain the strings

If the maximum number is not known of if the maximum length of a single string is not fixed, or if the strings can be input on successive lines, you will need to iterate with a simple loop:

    char buf[200];
    char **s = NULL;
    int n;

    while (scanf("%199s", buf) == 1) {
        char **s1 = realloc(s, (n + 1) * sizeof(*s));
        if (s1 == NULL || (s1[n] = strdup(buf)) == NULL) {
            printf("allocation error");
            exit(1);
        }
        s = s1;
        n++;
    }
    // `n` contains the number of strings
    // s[0], s[1]... contain pointers to the strings

Aside from the error handling, this loop is comparable to the hard-coded example above but it still has a maximum length for each string. Unless you can use a scanf() extension to allocate the strings automatically (%as on GNU systems), the code will be more complicated to handle any number of strings with any possible length.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
1

You can use:

  • fgets to read input from user. You have an easier time using this instead of scanf.
  • malloc to allocate memory for pointers on the heap. You can use a starting size, like in this example:

    size_t currsize = 10
    char **strings = malloc(currsize * sizeof(*strings)); /* always check
                                                             return value */
    

    and when space is exceeded, then realloc more space as needed:

    currsize *= 2;
    strings = realloc(strings, currsize * sizeof(*strings)); /* always check
                                                                return value */
    
  • When finished using the requested memory from malloc() and realloc(), it's always to good to free the pointers at the end.

  • strtok to parse the input at every space. When copying over the char * pointer from strtok(), you must also allocate space for strings[i], using malloc() or strdup.

Here is an example I wrote a while ago which does something very similar to what you want:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define INITSIZE 10
#define BUFFSIZE 100

int
main(void) {
    char **strings;
    size_t currsize = INITSIZE, str_count = 0, slen;

    char buffer[BUFFSIZE];
    char *word;
    const char *delim = " ";
    int i;

    /* Allocate initial space for array */
    strings = malloc(currsize * sizeof(*strings));
    if(!strings) {
        printf("Issue allocating memory for array of strings.\n");
        exit(EXIT_FAILURE);
    }

    printf("Enter some words(Press enter again to end): ");
    while (fgets(buffer, BUFFSIZE, stdin) != NULL && strlen(buffer) > 1) {

        /* grow array as needed */
        if (currsize == str_count) {
            currsize *= 2;
            strings = realloc(strings, currsize * sizeof(*strings));
            if(!strings) {
                printf("Issue reallocating memory for array of strings.\n");
                exit(EXIT_FAILURE);
            }
        }

        /* Remove newline from fgets(), and check for buffer overflow */
        slen = strlen(buffer);
        if (slen > 0) {
            if (buffer[slen-1] == '\n') {
                buffer[slen-1] = '\0';
            } else {
                printf("Exceeded buffer length of %d.\n", BUFFSIZE);
                exit(EXIT_FAILURE);
            }
        }

        /* Parsing of words from stdin */
        word = strtok(buffer, delim);
        while (word != NULL) {

            /* allocate space for one word, including nullbyte */
            strings[str_count] = malloc(strlen(word)+1);
            if (!strings[str_count]) {
                printf("Issue allocating space for word.\n");
                exit(EXIT_FAILURE);
            }

            /* copy strings into array */
            strcpy(strings[str_count], word);

            str_count++;
            word = strtok(NULL, delim);
        }
    }

    /* print and free strings */
    printf("Your array of strings:\n");
    for (i = 0; i < str_count; i++) {
        printf("strings[%d] = %s\n", i, strings[i]);
        free(strings[i]);
        strings[i] = NULL;
    }

    free(strings);
    strings = NULL;

    return 0;
}
RoadRunner
  • 25,803
  • 6
  • 42
  • 75