How to read a line from the console in C?
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.
pbreault
Updated on March 26, 2021Comments
-
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 over 15 yearsTHis doesn't allow for long strings... - which I think is the crux of his question.
-
Tim over 15 yearsCaveat - need to check result of realloc there. But if that fails, then there are worse problems most likely.
-
unwind over 15 years-1, gets() should not be used since it doesn't do bounds checking.
-
EloHailwidis over 15 yearsgets is an in secure function
-
Tim over 15 yearsYou should probably delete this answer or edit it before it kills your rep if you are concerned about that...
-
Paul Tomblin over 15 yearsYou 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 over 15 yearsPaul, 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 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 over 15 yearsOn 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 over 15 years@Tim - I want to keep all history :)
-
Paul Fisher over 15 yearsDon't forget to make the "100" into a constant or a #DEFINE in actual use.
-
jfs over 15 yearsOMG, why do you want to reinvent the wheel? Especially if the result is square wheels. google.com/…
-
Johannes Schaub - litb over 15 yearsSebastian, are you aware that is not a Standard C function? It's a GNU extension.
-
Johannes Schaub - litb over 15 yearsand, before i copy some random function from the internetz, i better write a function that works and whose caveats i know about. :)
-
Matt Gallagher almost 14 yearsThis 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 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 almost 12 yearsYour code would become MUCH simpler if you use
goto
to handle the error case. Nevertheless, don't you think you could reusetmp_buf
, instead ofmalloc
ing it with the same size over and over in the loop? -
NGix over 11 yearsIs it possible for
fgetc( stdin )
to get EOF value? -
autistic almost 9 years@NGix Apologies for the late response, which you've probably already found: Yep.
fgetc(stdin)
can returnEOF
, particularly whenstdin
is piped from a file or the user presses a key combination that closesstdin
(e.g. CTRL+d on Linux or CTRL+z on Windows). -
Panzercrisis over 8 yearsOne 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 -- Слава Україні about 8 yearsDownvoted.
gets
exists no more, thus this does not work in C11. -
wizzwizz4 over 7 yearsIf 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 about 7 yearsWhat is the purpose of
len
in here, when read provides the length too -
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 thanread
. -
Jonathan Leffler about 7 yearsNote that this
getline()
is different from the POSIX standardgetline()
function. -
Jonathan Leffler about 7 years@vladr: One option would be to add
flockfile()
before the first I/O operation and then ensure that it callsfunlockfile()
before any return, while usingfgetc_unlocked()
. This uses the speed of the unlocked calls without sacrificing thread-safety. All the POSIX functions that use aFILE *
must do the equivalent of this (flockfile()
plusfunlockfile()
) anyway — see the referenced specification. -
MKaama over 6 yearsHardly the simplest way.
-
Jonathan Leffler about 6 yearsYes, 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 about 6 yearsUsing 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 over 5 yearsWhy introduce linen without freeing it creating a memory leak? you could have just used
linep = realloc(linep,lenmax*2);
? -
KANJICODER about 3 yearsUnlike other answers, works on GCC , windows 10, compiled as C11 , 64bit.