Reputation: 215
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
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 free
ing 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
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