简体   繁体   中英

getting input with fgets() in a loop

I am trying to take input with fgets() . I know how many lines I will get but it changes and I store the number of lines in the variable var . I also have another variable named part ; it is the length of the line I get, but since there are white spaces between the values I multiplied it by 2 (I couldn't find another solution; I could use some advice).

Anyway, I tried to get the input as in the code below, but when I entered the first line it automatically breaks out the for loop and prints random things. I think it is to do with the fgets() in the loop; I don't know if there is a use of fgets() like this.

char inp[var][(2*part)];
int k,l;
for(k=0;k<=var;k++);
    fgets(inp[k],(2*part),stdin);
printf("%c\n",inp[0]);
printf("%c\n",inp[1]);
printf("%c\n",inp[2]);
printf("%c\n",inp[3]);

…since there are white spaces between the values I multiplied it with 2…

If you aren't required to store everything on the stack, you can instead store the strings in dynamically allocated memory. For example:

char* inp[var];
char buf[400]; // just needs to be long
for (k = 0; k < var;  k++) {
    fgets(buf, 400, stdin);
    inp[k] = malloc(sizeof(char) * (strlen(buf) + 1));
    strcpy(inp[k], buf);
}

Although technically not standards-compliant, strdup is widely available and makes this easier as well.

As far as the actual issue, as BLUEPIXY said in the comments above, you have a few typos.

  1. After the for loop, the semicolon makes it act unexpectedly.

     for(k=0;k<=var;k++); fgets(inp[k],(2*part),stdin); 

    is actually the same as

     for(k=0;k<=var;k++) { ; // do nothing } fgets(...); 

    Remove that semicolon after the for loop statement. As it is, you're not actually reading correctly, which is why you see garbage.

  2. To print an entire string, the printf family needs a %s format flag.

  3. With your bounds on k , there will actually be var + 1 iterations of the loop. If var were 3 , then k = 0,1,2,3 -> terminate when k checked at 4 .

Typically, the safest and easiest way to use fgets is to allocate a single, large-enough line buffer. Use that to read the line, then copy it into correctly sized buffers.

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

int main(void) {
    // Allocate just the space for the list, not the strings themselves.
    int num_input = 5;
    char *input[num_input];

    // Allocate our reusable line buffer.
    char line[1024];
    for( int i = 0; i < num_input; i++ ) {
        // Read into the line buffer.
        fgets(line, 1024,stdin);

        // Copy from the line buffer into correctly sized memory.
        input[i] = strdup(line);
    }

    for( int i = 0; i < num_input; i++ ) {
        printf("%s\n",input[i]);
    }
}

Note that strdup() is not an ISO C function, but POSIX. It's common and standard enough. It's too handy not to use. Write your own if necessary.

That takes care of not knowing the line length.


If you don't know the number of lines you're storing, you'll have to grow the array. Typically this is done with realloc to reallocate the existing memory. Start with a small list size, then grow it as needed. Doubling is a good rough approximation that's a pretty efficient balance between speed (reallocating can be slow) and memory efficiency.

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

int main(void) {
    // How big the input list is.
    size_t input_max = 64;

    // How many elements are in it.
    size_t input_size = 0;

    // Allocate initial memory for the input list.
    // Again, not for the strings, just for the list.
    char **input = malloc( sizeof(char*) * input_max );

    char line[1024];
    while( fgets(line, 1024,stdin) != NULL ) {
        // Check if we need to make the input list bigger.
        if( input_size >= input_max ) {
            // Double the max length.
            input_max *= 2;

            // Reallocate.
            // Note: this is only safe because we're
            // going to exit on error, otherwise we'd leak
            // input's memory.
            input = realloc( input, sizeof(char*) * input_max );

            // Check for error.
            if( input == NULL ) {
                fprintf(stderr, "Could not reallocate input list to %zu: %s", input_max, strerror(errno) );
                exit(1);
            }
        }

        input[input_size] = strdup(line);
        input_size++;
    }

    for( size_t i = 0; i < input_size; i++ ) {
        printf("%s\n",input[i]);
    }
}

As you can see, this gets a bit complicated. Now you need to keep track of the array, its maximum size, and its current size. Anyone using the array must remember to check its size and grow it, and remember to error check it. Your next impulse will be to create a struct to collect all that together, and functions to manage the list.

This is a good exercise in dynamic memory management, and I encourage you to do it. But for production code, use a pre-existing library. GLib is a good choice. It contains all sorts of handy data structures and functions that are missing from C, including pointer arrays that automatically grow . Use them, or something like it, in production code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM