lobengula3rd
lobengula3rd

Reputation: 1851

Reading an unknown length line from stdin in c with fgets

I am trying to read an unknown length line from stdin using the C language.

I have seen this when looking on the net:

char** str;
gets(&str);

But it seems to cause me some problems and I don't really understand how it is possible to do it this way.

Can you explain me why this example works/doesn't work and what will be the correct way to implement it (with malloc?)

Upvotes: 3

Views: 9409

Answers (5)

David Ranieri
David Ranieri

Reputation: 41017

You don't want a pointer to pointer to char, use an array of chars

char str[128];

or a pointer to char

char *str;

if you choose a pointer you need to reserve space using malloc

str = malloc(128);

Then you can use fgets

fgets(str, 128, stdin);

and remove the trailling newline

char *ptr = strchr(str, '\n');
if (ptr != NULL) *ptr = '\0';

To read an arbitrary long line, you can use getline (a function added to the GNU version of libc):

#define _GNU_SOURCE
#include <stdio.h>

char *foo(FILE * f)
{
    int n = 0, result;
    char *buf;

    result = getline(&buf, &n, f);
    if (result < 0) return NULL;
    return buf;
}

or your own implementation using fgets and realloc:

char *getline(FILE * f)
{
    size_t size = 0;
    size_t len  = 0;
    size_t last = 0;
    char *buf = NULL;

    do {
        size += BUFSIZ; /* BUFSIZ is defined as "the optimal read size for this platform" */
        buf = realloc(buf, size); /* realloc(NULL,n) is the same as malloc(n) */            
        /* Actually do the read. Note that fgets puts a terminal '\0' on the
           end of the string, so we make sure we overwrite this */
        if (buf == NULL) return NULL;
        fgets(buf + last, BUFSIZ, f);
        len = strlen(buf);
        last = len - 1;
    } while (!feof(f) && buf[last] != '\n');
    return buf;
}

Call it using

char *str = getline(stdin);

if (str == NULL) {
    perror("getline");
    exit(EXIT_FAILURE);
}
...
free(str);

More info

Upvotes: 6

ryyker
ryyker

Reputation: 23208

"Reading an unknown length line from stdin in c with fgets"

Late response - A Windows approach:

The OP does not specify Linux or Windows, but the viable answers posted in response for this question all seem to have the getline() function in common, which is POSIX only. Functions such as getline() and popen() are very useful and powerful but sadly are not included in Windows environments.

Consequently, implementing such a task in a Windows environment requires a different approach. The link here describes a method that can read input from stdin and has been tested up to 1.8 gigabytes on the system it was developed on. (Also described in the link.)_ The simple code snippet below was tested using the following command line to read large quantities on stdin:

cd c:\dev && dir /s  // approximately 1.8Mbyte buffer is returned on my system 

Simple example:

#include "cmd_rsp.h"
int main(void)
{
    char *buf = {0};
    buf = calloc(100, 1);//initialize buffer to some small value
    if(!buf)return 0;
    cmd_rsp("dir /s", &buf, 100);//recursive directory search on Windows system
    printf("%s", buf);
    free(buf);
    
    return 0;
}

cmd_rsp() is fully described in the links above, but it is essentially a Windows implementation that includes popen() and getline() like capabilities, packaged up into this very simple function.

Upvotes: 0

user3629249
user3629249

Reputation: 16540

use the getline() function, this will return the length of the line, and a pointer to the contents of the line in an allocated memory area. (be sure to pass the line pointer to free() when done with it )

Upvotes: 1

Pankaj Andhale
Pankaj Andhale

Reputation: 403

if u want to input an unknown length of string or input try using following code.

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


int main()
{
char  *m;
clrscr();
printf("please input a string\n");
scanf("%ms",&m);
if (m == NULL)
    fprintf(stderr, "That string was too long!\n");
else
{
    printf("this is the string %s\n",m);
    /* ... any other use of m */
    free(m);


}

getch();
return 0;

}

Note that %ms, %as are GNU extensions..

Upvotes: -3

Peter
Peter

Reputation: 36597

Firstly, gets() provides no way of preventing a buffer overrun. That makes it so dangerous it has been removed from the latest C standard. It should not be used. However, the usual usage is something like

char buffer[20];
gets(buffer);      /*  pray that user enters no more than 19 characters in a line */

Your usage is passing gets() a pointer to a pointer to a pointer to char. That is not what gets() expects, so your code would not even compile.

That element of prayer reflected in the comment is why gets() is so dangerous. If the user enters 20 (or more) characters, gets() will happily write data past the end of buffer. There is no way a programmer can prevent that in code (short of accessing hardware to electrocute the user who enters too much data, which is outside the realm of standard C).

To answer your question, however, the only ways involve allocating a buffer of some size, reading data in some controlled way until that size is reached, reallocating if needed to get a greater size, and continuing until a newline (or end-of-file, or some other error condition on input) is encountered.

malloc() may be used for the initial allocation. malloc() or realloc() may be used for the reallocation (if needed). Bear in mind that a buffer allocated this way must be released (using free()) when the data is no longer needed - otherwise the result is a memory leak.

Upvotes: 3

Related Questions