How to read a line from the console in C?

Viewed 326813

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.

14 Answers

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

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.

A very simple but unsafe implementation to read line for static allocation:

char line[1024];

scanf("%[^\n]", line);

A safer implementation, without the possibility of buffer overflow, but with the possibility of not reading the whole line, is:

char line[1024];

scanf("%1023[^\n]", line);

Not the 'difference by one' between the length specified declaring the variable and the length specified in the format string. It is a historical artefact.

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 ;)

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

As suggested, you can use getchar() to read from the console until an end-of-line or an EOF is returned, building your own buffer. Growing buffer dynamically can occur if you are unable to set a reasonable maximum line size.

You can use also use fgets as a safe way to obtain a line as a C null-terminated string:

#include <stdio.h>

char line[1024];  /* Generously large value for most situations */

char *eof;

line[0] = '\0'; /* Ensure empty line if no input delivered */
line[sizeof(line)-1] = ~'\0';  /* Ensure no false-null at end of buffer */

eof = fgets(line, sizeof(line), stdin);

If you have exhausted the console input or if the operation failed for some reason, eof == NULL is returned and the line buffer might be unchanged (which is why setting the first char to '\0' is handy).

fgets will not overfill line[] and it will ensure that there is a null after the last-accepted character on a successful return.

If end-of-line was reached, the character preceding the terminating '\0' will be a '\n'.

If there is no terminating '\n' before the ending '\0' it may be that there is more data or that the next request will report end-of-file. You'll have to do another fgets to determine which is which. (In this regard, looping with getchar() is easier.)

In the (updated) example code above, if line[sizeof(line)-1] == '\0' after successful fgets, you know that the buffer was filled completely. If that position is proceeded by a '\n' you know you were lucky. Otherwise, there is either more data or an end-of-file up ahead in stdin. (When the buffer is not filled completely, you could still be at an end-of-file and there also might not be a '\n' at the end of the current line. Since you have to scan the string to find and/or eliminate any '\n' before the end of the string (the first '\0' in the buffer), I am inclined to prefer using getchar() in the first place.)

Do what you need to do to deal with there still being more line than the amount you read as the first chunk. The examples of dynamically-growing a buffer can be made to work with either getchar or fgets. There are some tricky edge cases to watch out for (like remembering to have the next input start storing at the position of the '\0' that ended the previous input before the buffer was extended).

I came across the same problem some time ago, this was my solutuion, hope it helps.

/*
 * Initial size of the read buffer
 */
#define DEFAULT_BUFFER 1024

/*
 * Standard boolean type definition
 */
typedef enum{ false = 0, true = 1 }bool;

/*
 * Flags errors in pointer returning functions
 */
bool has_err = false;

/*
 * Reads the next line of text from file and returns it.
 * The line must be free()d afterwards.
 *
 * This function will segfault on binary data.
 */
char *readLine(FILE *file){
    char *buffer   = NULL;
    char *tmp_buf  = NULL;
    bool line_read = false;
    int  iteration = 0;
    int  offset    = 0;

    if(file == NULL){
        fprintf(stderr, "readLine: NULL file pointer passed!\n");
        has_err = true;

        return NULL;
    }

    while(!line_read){
        if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
            fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
            free(tmp_buf);

            break;
        }

        if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
            line_read = true;

        offset = DEFAULT_BUFFER * (iteration + 1);

        if((buffer = realloc(buffer, offset)) == NULL){
            fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
            free(tmp_buf);
            has_err = true;

            return NULL;
        }

        offset = DEFAULT_BUFFER * iteration - iteration;

        if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
            fprintf(stderr, "readLine: Cannot copy to buffer\n");
            free(tmp_buf);
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        free(tmp_buf);
        iteration++;
    }

    return buffer;
}

The best and simplest way to read a line from a console is using the getchar() function, whereby you will store one character at a time in an array.

{
char message[N];        /* character array for the message, you can always change the character length */
int i = 0;          /* loop counter */

printf( "Enter a message: " );
message[i] = getchar();    /* get the first character */
while( message[i] != '\n' ){
    message[++i] = getchar(); /* gets the next character */
}

printf( "Entered message is:" );
for( i = 0; i < N; i++ )
    printf( "%c", message[i] );

return ( 0 );

}

Here is a minimal implementation to do it, the nice thing is that it will not keep the '\n', however you have to give it a size to read for security:

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

int sc_gets(char *buf, int n)
{
    int count = 0;
    char c;

    if (__glibc_unlikely(n <= 0))
        return -1;

    while (--n && (c = fgetc(stdin)) != '\n')
        buf[count++] = c;
    buf[count] = '\0';

    return (count != 0 || errno != EAGAIN) ? count : -1;
}

Test with:

#define BUFF_SIZE 10

int main (void) {
    char buff[BUFF_SIZE];

    sc_gets(buff, sizeof(buff));
    printf ("%s\n", buff);

    return 0;
}

NB: You are limited to INT_MAX to find your line return, which is more than enough.

There is a simple regex like syntax that can be used inside scanf to take whole line as input

scanf("%[^\n]%*c", str); ^\n tells to take input until newline doesn't get encountered. Then, with %*c, it reads newline character and here used * indicates that this newline character is discarded.

Sample code

#include <stdio.h>
int main()
{
   char S[101];
   scanf("%[^\n]%*c", S);
   printf("%s", S);
   return 0;
}
Related