How to read a line from the console in C?

315,030

Solution 1

You need dynamic memory management, and use the fgets function to read your line. However, there seems to be no way to see how many characters it read. So you use fgetc:

char * getline(void) {
    char * line = malloc(100), * linep = line;
    size_t lenmax = 100, len = lenmax;
    int c;

    if(line == NULL)
        return NULL;

    for(;;) {
        c = fgetc(stdin);
        if(c == EOF)
            break;

        if(--len == 0) {
            len = lenmax;
            char * linen = realloc(linep, lenmax *= 2);

            if(linen == NULL) {
                free(linep);
                return NULL;
            }
            line = linen + (line - linep);
            linep = linen;
        }

        if((*line++ = c) == '\n')
            break;
    }
    *line = '\0';
    return linep;
}

Note: Never use gets ! It does not do bounds checking and can overflow your buffer

Solution 2

If you are using the GNU C library or another POSIX-compliant library, you can use getline() and pass stdin to it for the file stream.

Solution 3

So, if you were looking for command arguments, take a look at Tim's answer. If you just want to read a line from console:

#include <stdio.h>

int main()
{
  char string [256];
  printf ("Insert your full address: ");
  gets (string);
  printf ("Your address is: %s\n",string);
  return 0;
}

Yes, it is not secure, you can do buffer overrun, it does not check for end of file, it does not support encodings and a lot of other stuff. Actually I didn't even think whether it did ANY of this stuff. I agree I kinda screwed up :) But...when I see a question like "How to read a line from the console in C?", I assume a person needs something simple, like gets() and not 100 lines of code like above. Actually, I think, if you try to write those 100 lines of code in reality, you would do many more mistakes, than you would have done had you chosen gets ;)

Solution 4

getline runnable example

getline was mentioned on this answer but here is an example.

It is POSIX 7, allocates memory for us, and reuses the allocated buffer on a loop nicely.

Pointer newbs, read this: Why is the first argument of getline a pointer to pointer "char**" instead of "char*"?

main.c

#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char *line = NULL;
    size_t len = 0;
    ssize_t read = 0;
    while (1) {
        puts("enter a line");
        read = getline(&line, &len, stdin);
        if (read == -1)
            break;
        printf("line = %s", line);
        printf("line length = %zu\n", read);
        puts("");
    }
    free(line);
    return 0;
}

Compile and run:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out

Outcome: this shows on therminal:

enter a line

Then if you type:

asdf

and press enter, this shows up:

line = asdf
line length = 5

followed by another:

enter a line

Or from a pipe to stdin:

printf 'asdf\nqwer\n' | ./main.out

gives:

enter a line
line = asdf
line length = 5

enter a line
line = qwer
line length = 5

enter a line

Tested on Ubuntu 20.04.

glibc implementation

No POSIX? Maybe you want to look at the glibc 2.23 implementation.

It resolves to getdelim, which is a simple POSIX superset of getline with an arbitrary line terminator.

It doubles the allocated memory whenever increase is needed, and looks thread-safe.

It requires some macro expansion, but you're unlikely to do much better.

Solution 5

You might need to use a character by character (getc()) loop to ensure you have no buffer overflows and don't truncate the input.

Share:
315,030
pbreault
Author by

pbreault

Updated on March 26, 2021

Comments

  • pbreault
    pbreault about 3 years

    What is the simplest way to read a full line in a C console program The text entered might have a variable length and we can't make any assumption about its content.

  • Tim
    Tim over 15 years
    THis doesn't allow for long strings... - which I think is the crux of his question.
  • Tim
    Tim over 15 years
    Caveat - need to check result of realloc there. But if that fails, then there are worse problems most likely.
  • unwind
    unwind over 15 years
    -1, gets() should not be used since it doesn't do bounds checking.
  • EloHailwidis
    EloHailwidis over 15 years
    gets is an in secure function
  • Tim
    Tim over 15 years
    You should probably delete this answer or edit it before it kills your rep if you are concerned about that...
  • Paul Tomblin
    Paul Tomblin over 15 years
    You could probably improve the efficiency a bit by doing fgets with buffer, and checking if you have the newline character at the end. If you don't, realloc your accumulation buffer, copy into it, and fgets again.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 15 years
    Paul, what guarantees me that there is a newline or '\0' at the end if fgets doesn't need more space? it could have read less than the buffer size. A wait. you mean using strlen to find the \0 and looking before it? Well wasn't sure what is faster. So i did go with this char-by-char way.
  • Adam Rosenfield
    Adam Rosenfield over 15 years
    @litb: this is C, not C++. There are no exceptions (in the C++ sense) and no concept of exception-safety.
  • Martin Beckett
    Martin Beckett over 15 years
    On the other hand if you are writing a program for yourself and just need to read an input this is perfectly fine. How much security a program needs is par tof the spec - you don't HAVe to put it as a priority everytime.
  • Paul Kapustin
    Paul Kapustin over 15 years
    @Tim - I want to keep all history :)
  • Paul Fisher
    Paul Fisher over 15 years
    Don't forget to make the "100" into a constant or a #DEFINE in actual use.
  • jfs
    jfs over 15 years
    OMG, why do you want to reinvent the wheel? Especially if the result is square wheels. google.com/…
  • Johannes Schaub - litb
    Johannes Schaub - litb over 15 years
    Sebastian, are you aware that is not a Standard C function? It's a GNU extension.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 15 years
    and, before i copy some random function from the internetz, i better write a function that works and whose caveats i know about. :)
  • Matt Gallagher
    Matt Gallagher almost 14 years
    This function needs a correction: the line "len = lenmax;" after the realloc should either precede the realloc or should be "len = lenmax >> 1;" -- or some other equivalent that accounts for the fact that half the length is already used.
  • vladr
    vladr about 12 years
    @Johannes, in answer to your question, @Paul's approach COULD be faster on most (i.e. reentrant) libc implementations, since your approach implicitly locks stdin for every character whereas his locks it once per buffer. You could use the less portable fgetc_unlocked if thread safety is not a concern but performance is.
  • Shahbaz
    Shahbaz almost 12 years
    Your code would become MUCH simpler if you use goto to handle the error case. Nevertheless, don't you think you could reuse tmp_buf, instead of mallocing it with the same size over and over in the loop?
  • NGix
    NGix over 11 years
    Is it possible for fgetc( stdin ) to get EOF value?
  • autistic
    autistic almost 9 years
    @NGix Apologies for the late response, which you've probably already found: Yep. fgetc(stdin) can return EOF, particularly when stdin is piped from a file or the user presses a key combination that closes stdin (e.g. CTRL+d on Linux or CTRL+z on Windows).
  • Panzercrisis
    Panzercrisis over 8 years
    One thing I do wonder about is the efficiency of this vs. something like fgets(). The answer's highly upvoted, so are they roughly equivalent?
  • Antti Haapala -- Слава Україні
    Antti Haapala -- Слава Україні about 8 years
    Downvoted. gets exists no more, thus this does not work in C11.
  • wizzwizz4
    wizzwizz4 over 7 years
    If this even goes anywhere near a production environment, end-users will get either cross or hacked. This is not a good function to ever use.
  • Honinbo Shusaku
    Honinbo Shusaku about 7 years
    What is the purpose of len in here, when read provides the length too
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com about 7 years
    @Abdul see man getline. len is the length of the existing buffer, 0 is magic and tells it to allocate. Read is number of chars read. The buffer size could be larger than read.
  • Jonathan Leffler
    Jonathan Leffler about 7 years
    Note that this getline() is different from the POSIX standard getline() function.
  • Jonathan Leffler
    Jonathan Leffler about 7 years
    @vladr: One option would be to add flockfile() before the first I/O operation and then ensure that it calls funlockfile() before any return, while using fgetc_unlocked(). This uses the speed of the unlocked calls without sacrificing thread-safety. All the POSIX functions that use a FILE * must do the equivalent of this (flockfile() plus funlockfile()) anyway — see the referenced specification.
  • MKaama
    MKaama over 6 years
    Hardly the simplest way.
  • Jonathan Leffler
    Jonathan Leffler about 6 years
    Yes, the function exists. The caveat that it does not provide a null-terminated string is sufficiently large and problematic that it is probably better not to use it — it's dangerous.
  • Jonathan Leffler
    Jonathan Leffler about 6 years
    Using a single global variable has_err to report errors makes this function thread-unsafe and less than comfortable to use. Don't do it that way. You already indicate an error by returning NULL. There is also room to think that the printed error messages are not a good idea in a general-purpose library function.
  • rmoro
    rmoro over 5 years
    Why introduce linen without freeing it creating a memory leak? you could have just used linep = realloc(linep,lenmax*2); ?
  • KANJICODER
    KANJICODER about 3 years
    Unlike other answers, works on GCC , windows 10, compiled as C11 , 64bit.