Jumper
Jumper

Reputation: 215

Taking user input and storing it in an array of strings in C

I'm new to C and trying to write a command line application that does a whole host of things based on the user input. I need to run the program on an infinite loop and read user input into a string array.

I've used the while loop to write it into a string.

while(fgets(str, 256, stdin)){

}

Now I'm a little confused about how to modify this loop to write strings into an array directly using space as a delimiter.

So if I've an input

Terminate client 2345

The array should have 3 elements with the first being Terminate. Any help appreciated.

Upvotes: 1

Views: 1444

Answers (2)

David C. Rankin
David C. Rankin

Reputation: 84642

The process is basically the same regardless of whether you read from stdin or read from a file. You either read each line with line-oriented input (fgets or getline) or you use character-oriented input (getchar, fgetc, etc). (the scanf family falls in the middle). When reading lines, generally the best choice is line-oriented input.

When reading user input into an array, you have two choices, either declare a static array of pointers and hope you allocated enough pointers to begin with, or you dynamically allocate the array of pointers and realloc as needed to hold all input. (who knows, the user might redirect a file for reading). When you allocate anything dynamically you are responsible to track its use, preserve a pointer to the original starting address for the block of memory, and freeing the memory when it is no longer needed.

The following is a standard example of taking input from stdin and storing it in a dynamically allocated array. There is a twist. The code can handle input from a file or stdin. If a filename is given as the first argument, then it will read the file, otherwise it reads from stdin. It expects the user to provide as much input as required, and then press [ctrl+d] when done. (manually generating EOF).

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

#define NMAX 128

int main (int argc, char **argv) {

    char *ln = NULL;                /* NULL forces getline to allocate  */
    size_t n = 0;                   /* initial ln size, getline decides */
    ssize_t nchr = 0;               /* number of chars actually read    */
    size_t idx = 0;                 /* array index counter              */
    size_t nmax = NMAX;             /* check for reallocation           */
    char **array = NULL;            /* array to hold lines read         */
    FILE *fp = NULL;                /* file pointer to open file fn     */

    if (argc > 1) {
        if (!(fp = fopen (argv[1], "r"))) {
            fprintf (stderr, "error: file open failed for '%s'\n", argv[1]);
            return 1;
        }
    }
    else
        fp = stdin;

    /* allocate NMAX pointers to char* */
    if (!(array = calloc (NMAX, sizeof *array))) {
        fprintf (stderr, "error: memory allocation failed.");
        return 1;
    }

    if (fp == stdin)
        printf ("\nEnter information to store in array on each line, [ctrl+d] when done:\n\n");

    /* read each line from file or stdin - dynamicallly allocated   */
    while ((nchr = getline (&ln, &n, fp)) != -1)
    {
        /* strip newline or carriage rtn    */
        while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
            ln[--nchr] = 0;

        array[idx] = strdup (ln);   /* allocate/copy ln to array        */

        idx++;                      /* increment value at index         */

        if (idx == nmax) {          /* if lines exceed nmax, reallocate */
            char **tmp = realloc (array, nmax * 2 * sizeof *tmp);
            if (!tmp) {
                fprintf (stderr, "error: memory exhausted.\n");
                break;
            }
            array = tmp;
            nmax *= 2;
        }
    }

    if (ln) free (ln);              /* free memory allocated by getline */
    if (fp != stdin) fclose (fp);   /* close open file descriptor       */

    size_t i = 0;

    /* print array */
    printf ("\nThe lines in the file are:\n\n");
    for (i = 0; i < idx; i++)
        printf (" line[%3zu] : %s\n", i, array[i]);

    /* free array */
    for (i = 0; i < idx; i++)
        free (array[i]);
    free (array);

    return 0;
}

Example/Output

$ ./bin/getline_readstdin_dyn

Enter information to store in array on each line, [ctrl+d] when done:

This is a line of input
This is another
and another
etc..

The lines in the file are:

 line[  0] : This is a line of input
 line[  1] : This is another
 line[  2] : and another
 line[  3] : etc..

Or reading from a file:

$ ./bin/getline_readstdin_dyn dat/ll_replace_poem.txt

The lines in the file are:

 line[  0] : Eye have a spelling chequer,
 line[  1] : It came with my Pea Sea.
 line[  2] : It plane lee marks four my revue,
 line[  3] : Miss Steaks I can knot sea.
 line[  4] : Eye strike the quays and type a whirred,
 line[  5] : And weight four it two say,
 line[  6] : Weather eye am write oar wrong,
 line[  7] : It tells me straight aweigh.
 line[  8] : Eye ran this poem threw it,
 line[  9] : Your shore real glad two no.
 line[ 10] : Its vary polished in its weigh.
 line[ 11] : My chequer tolled me sew.
 line[ 12] : A chequer is a bless thing,
 line[ 13] : It freeze yew lodes of thyme.
 line[ 14] : It helps me right all stiles of righting,
 line[ 15] : And aides me when eye rime.
 line[ 16] : Each frays come posed up on my screen,
 line[ 17] : Eye trussed too bee a joule.
 line[ 18] : The chequer pours over every word,
 line[ 19] : Two cheque sum spelling rule.

Redirecting input is fine as well. E.g.:

$ ./bin/getline_readstdin_dyn < dat/ll_replace_poem.txt

Upvotes: 3

Joshua
Joshua

Reputation: 43327

At the risk of advising parse kludge, you have your read loop. Now use strtok or sscanf to parse string by tokens into storage variables. In this setup I suspect strdup is the better choice.

Upvotes: 0

Related Questions