C: counting the string's length before using scanf - c

I am trying to find a way to manipulate strings in C in a more efficient way (maybe like how java does it).
One way I thought of it is to count the size of the string till the end of the line (maybe including spaces), allocate memory of this size using malloc() and then go back to the beginning of the line and scan the string.
Is there a way to do this? I don't know if there is a way to return the "cursor" to the beginning of the line to 're'scan something.
And if you know another/better way to deal with strings in C please tell me.
Thanks

There is no way to do what you're asking directly, but there is a (in my opinion far better) alternative: fgets().
What it does is read the text until the end of the line, including the final line-feed. If the line is longer than the buffer, then it omits that line feed --- you can use that fact to check if the line was completed.
Something like this (UNTESTED CODE):
// WARNING: Example does not include error checking
// (check the return value of `fgets()`, `malloc()` and `realloc()`!)
size_t buflen = 64;
size_t pos = 0;
char* buf = malloc(buflen);
// `for(;;)` is an infinite loop
for(;;)
{
// read data into buf[pos..buflen] (total of `buflen-pos` bytes)
fgets(buf + pos, buflen - pos, file);
pos = pos + strcspn(buf + pos, "\r\n");
if(buf[pos]) // reached end of line; end the loop
break;
buflen += 64;
// alternative (double the size):
// buflen <<= 1;
buf = realloc(buf, buflen); // resize the buffer
}
// `buf` contains our line; `pos` contains the end of it
// optional: remove the trailing newline
// buf[pos] = 0;
Relevant documentation:
fgets()
strcspn()
malloc()
realloc()

You could use scanf to read every character and then add that character into your buffer.
Your buffer initial size could be 16. And after you read every character you check if you have space for that new character. If you do not have space for your new character you double buffer size and realloc it.
Check out the code example:
#include <stdio.h>
#include <stdlib.h>
char *str;
int main(void) {
char c = '\0';
int size = 0;
int buffer_size = 16;
str = (char *) calloc(buffer_size, sizeof(char));
while (c != '\n') {
scanf("%c", &c);
if (size + 1 == buffer_size) {
buffer_size *= 2;
str = (char *) realloc(str, buffer_size);
if (str == NULL) {
fprintf(stderr, "insufficient memory\n");
return EXIT_FAILURE;
}
}
str[size] = c;
size++;
}
printf("%s\n", str);
return EXIT_SUCCESS;
}

Related

Using fgets with realloc()

I'm trying to create a function to read a single line from a file of text using fgets() and store it in a dynamically allocating char* using malloc()but I am unsure as to how to use realloc() since I do not know the length of this single line of text and do not want to just guess a magic number for the maximum size that this line could possibly be.
#include "stdio.h"
#include "stdlib.h"
#define INIT_SIZE 50
void get_line (char* filename)
char* text;
FILE* file = fopen(filename,"r");
text = malloc(sizeof(char) * INIT_SIZE);
fgets(text, INIT_SIZE, file);
//How do I realloc memory here if the text array is full but fgets
//has not reach an EOF or \n yet.
printf(The text was %s\n", text);
free(text);
int main(int argc, char *argv[]) {
get_line(argv[1]);
}
I am planning on doing other things with the line of text but for sake of keeping this simple, I have just printed it and then freed the memory.
Also: The main function is initiated by using the filename as the first command line argument.
The getline function is what you looking for.
Use it like this:
char *line = NULL;
size_t n;
getline(&line, &n, stdin);
If you really want to implement this function yourself, you can write something like this:
#include <stdlib.h>
#include <stdio.h>
char *get_line()
{
int c;
/* what is the buffer current size? */
size_t size = 5;
/* How much is the buffer filled? */
size_t read_size = 0;
/* firs allocation, its result should be tested... */
char *line = malloc(size);
if (!line)
{
perror("malloc");
return line;
}
line[0] = '\0';
c = fgetc(stdin);
while (c != EOF && c!= '\n')
{
line[read_size] = c;
++read_size;
if (read_size == size)
{
size += 5;
char *test = realloc(line, size);
if (!test)
{
perror("realloc");
return line;
}
line = test;
}
c = fgetc(stdin);
}
line[read_size] = '\0';
return line;
}
One possible solution is to use two buffers: One temporary that you use when calling fgets; And one that you reallocate, and append the temporary buffer to.
Perhaps something like this:
char temp[INIT_SIZE]; // Temporary string for fgets call
char *text = NULL; // The actual and full string
size_t length = 0; // Current length of the full string, needed for reallocation
while (fgets(temp, sizeof temp, file) != NULL)
{
// Reallocate
char *t = realloc(text, length + strlen(temp) + 1); // +1 for terminator
if (t == NULL)
{
// TODO: Handle error
break;
}
if (text == NULL)
{
// First allocation, make sure string is properly terminated for concatenation
t[0] = '\0';
}
text = t;
// Append the newly read string
strcat(text, temp);
// Get current length of the string
length = strlen(text);
// If the last character just read is a newline, we have the whole line
if (length > 0 && text[length - 1] == '\n')
{
break;
}
}
[Discalimer: The code above is untested and may contain bugs]
With the declaration of void get_line (char* filename), you can never make use of the line you read and store outside of the get_line function because you do not return a pointer to line and do not pass the address of any pointer than could serve to make any allocation and read visible back in the calling function.
A good model (showing return type and useful parameters) for any function to read an unknown number of characters into a single buffer is always POSIX getline. You can implement your own using either fgetc of fgets and a fixed buffer. Efficiency favors the use of fgets only to the extent it would minimize the number of realloc calls needed. (both functions will share the same low-level input buffer size, e.g. see gcc source IO_BUFSIZ constant -- which if I recall is now LIO_BUFSIZE after a recent name change, but basically boils down to an 8192 byte IO buffer on Linux and 512 bytes on windows)
So long as you dynamically allocate the original buffer (either using malloc, calloc or realloc), you can read continually with a fixed buffer using fgets adding the characters read into the fixed buffer to your allocated line and checking whether the final character is '\n' or EOF to determine when you are done. Simply read a fixed buffer worth of chars with fgets each iteration and realloc your line as you go, appending the new characters to the end.
When reallocating, always realloc using a temporary pointer. That way, if you run out of memory and realloc returns NULL (or fails for any other reason), you won't overwrite the pointer to your currently allocated block with NULL creating a memory leak.
A flexible implementation that sizes the fixed buffer as a VLA using either the defined SZINIT for the buffer size (if the user passes 0) or the size provided by the user to allocate initial storage for line (passed as a pointer to pointer to char) and then reallocating as required, returning the number of characters read on success or -1 on failure (the same as POSIX getline does) could be done like:
/** fgetline, a getline replacement with fgets, using fixed buffer.
* fgetline reads from 'fp' up to including a newline (or EOF)
* allocating for 'line' as required, initially allocating 'n' bytes.
* on success, the number of characters in 'line' is returned, -1
* otherwise
*/
ssize_t fgetline (char **line, size_t *n, FILE *fp)
{
if (!line || !n || !fp) return -1;
#ifdef SZINIT
size_t szinit = SZINIT > 0 ? SZINIT : 120;
#else
size_t szinit = 120;
#endif
size_t idx = 0, /* index for *line */
maxc = *n ? *n : szinit, /* fixed buffer size */
eol = 0, /* end-of-line flag */
nc = 0; /* number of characers read */
char buf[maxc]; /* VLA to use a fixed buffer (or allocate ) */
clearerr (fp); /* prepare fp for reading */
while (fgets (buf, maxc, fp)) { /* continuall read maxc chunks */
nc = strlen (buf); /* number of characters read */
if (idx && *buf == '\n') /* if index & '\n' 1st char */
break;
if (nc && (buf[nc - 1] == '\n')) { /* test '\n' in buf */
buf[--nc] = 0; /* trim and set eol flag */
eol = 1;
}
/* always realloc with a temporary pointer */
void *tmp = realloc (*line, idx + nc + 1);
if (!tmp) /* on failure previous data remains in *line */
return idx ? (ssize_t)idx : -1;
*line = tmp; /* assign realloced block to *line */
memcpy (*line + idx, buf, nc + 1); /* append buf to line */
idx += nc; /* update index */
if (eol) /* if '\n' (eol flag set) done */
break;
}
/* if eol alone, or stream error, return -1, else length of buf */
return (feof (fp) && !nc) || ferror (fp) ? -1 : (ssize_t)idx;
}
(note: since nc already holds the current number of characters in buf, memcpy can be used to append the contents of buf to *line without scanning for the terminating nul-character again) Look it over and let me know if you have further questions.
Essentially you can use it as a drop-in replacement for POSIX getline (though it will not be quite as efficient -- but isn't not bad either)

C fgets read lines

please, could you help me, how to read lines from stdin via fgets? Problem is, that sometimes in tmp_string are stored part of data from two lines before etc... I'm using this code to load line:
int loadLine(Line *line) {
char tmp_string[MAX_LOAD];
int return_val;
return_val = 0;
line->length = MAX_LOAD;
line->index = 0;
line->data = (char*) malloc(sizeof (char) * line->length);
while (fgets(tmp_string, MAX_LOAD - 1, stdin) != NULL) {
strncat(line->data, tmp_string, MAX_LOAD);
line->index += strlen(tmp_string);
if (tmp_string[strlen(tmp_string) - 1] == '\n') { /* if I'm at the end of line... */
line->data[strlen(line->data) - 1] = '\0';
return_val = 1;
break;
}
if ((line->index + MAX_LOAD) > line->length) {
resizeLine(line);
}
}
if(feof(stdin))
{
return_val = 0;
}
return return_val;
}
And here is usage:
Line line;
if(loadLine(&line) == 0){ ... }
free(line.data);
line->data has not been initialised (no terminator), so strncat() can fail.
line->data = malloc(sizeof (char) * line->length);
line->data[0] = 0;
Answering the question you posed, your usage of fgets() looks reasonable, though you are wasting one byte of your tmp_string variable. The fgets() function already knows to reserve one byte for a trailing NUL, so if you are reading into an array of length MAX_LOAD then you would normally use
fgets(tmp_string, MAX_LOAD, stdin);
This will overwrite the previous contents of the destination buffer (tmp_string) up to the number of bytes read plus one, and it will automatically add a string terminator after the last byte read. If a previously read line was longer then its tail might remain in the buffer, but that shouldn't matter because the standard string functions will ignore anything after the terminator.
The misbehavior you see is very likely caused by your failure to initialize line->data to a zero-length string before passing it to strncat(), as Weather Vane observed first.

How do I use scanf when I dont know how many values it will assign in C?

These are the instructions:
"Read characters from standard input until EOF (the end-of-file mark) is read. Do not prompt the user to enter text - just read data as soon as the program starts."
So the user will be entering characters, but I dont know how many. I will later need to use them to build a table that displays the ASCII code of each value entered.
How should I go about this?
This is my idea
int main(void){
int inputlist[], i = -1;
do {++i;scanf("%f",&inputlist[i]);}
while(inputlist[i] != EOF)
You said character.So this might be used
char arr[10000];
ch=getchar();
while(ch!=EOF)
{
arr[i++]=ch;
ch=getchar();
}
//arr[i]=0; TO make it a string,if necessary.
And to convert to ASCII
for(j=0;j<i;j++)
printf("%d\n",arr[j]);
If you are particular in using integer array,Use
int arr[1000];
while(scanf("%d",&arr[i++])!=EOF);
PPS:This works only if your input is one character per line.
scanf returns EOF on EOF
You have a reasonable attempt at a start to the solution, with a few errors. You can't define an array without specifying a size, so int inputlist[] shouldn't even compile. Your scanf() specifier is %f for float, which is wrong twice (once because you declared inputlist with an integer type, and twice because you said your input is characters, so you should be telling scanf() to use %c or %s), and really if you're reading input unconditionally until EOF, you should use an unconditional input function, such as fgets() or fread(). (or read(), if you prefer).
You'll need two things: A place to store the current chunk of input, and a place to store the input that you've already read in. Since the input functions I mentioned above expect you to specify the input buffer, you can allocate that with a simple declaration.
char input[1024];
However, for the place to store all input, you'll want something dynamically allocated. The simplest solution is to simply malloc() a chunk of storage, keep track of how large it is, and realloc() it if and when necessary.
char *all_input;
int poolsize=16384;
all_input = malloc(pool_size);
Then, just loop on your input function until the return value indicates that you've hit EOF, and on each iteration of the loop, append the input data to the end of your storage area, increment a counter by the size of the input data, and check whether you're getting too close to the size of your input storage area. (And if you are, then use realloc() to grow your storage.)
You could read the input by getchar until reach EOF. And you don't know the size of input, you should use dynamic size buffer in heap.
char *buf = NULL;
long size = 1024;
long count = 0;
char r;
buf = (char *)malloc(size);
if (buf == NULL) {
fprintf(stderr, "malloc failed\n");
exit(1);
}
while( (r = getchar()) != EOF) {
buf[count++] = r;
// leave one space for '\0' to terminate the string
if (count == size - 1) {
buf = realloc(buf,size*2);
if (buf == NULL) {
fprintf(stderr, "realloc failed\n");
exit(1);
}
size = size * 2;
}
}
buf[count] = '\0';
printf("%s \n", buf);
return 0;
Here is full solution for your needs with comments.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
// Number of elements
#define CHARNUM 3
int main(int argc, char **argv) {
// Allocate memory for storing input data
// We calculate requested amount of bytes by the formula:
// NumElement * SizeOfOneElement
size_t size = CHARNUM * sizeof(int);
// Call function to allocate memory
int *buffer = (int *) calloc(1, size);
// Check that calloc() returned valid pointer
// It can: 1. Return pointer in success or NULL in faulire
// 2. Return pointer or NULL if size is 0
// (implementation dependened).
// We can't use this pointer later.
if (!buffer || !size)
{
exit(EXIT_FAILURE);
}
int curr_char;
int count = 0;
while ((curr_char = getchar()) != EOF)
{
if (count >= size/sizeof(int))
{
// If we put more characters than now our buffer
// can hold, we allocate more memory
fprintf(stderr, "Reallocate memory buffer\n");
size_t tmp_size = size + (CHARNUM * sizeof(int));
int *tmp_buffer = (int *) realloc(buffer, tmp_size);
if (!tmp_buffer)
{
fprintf(stderr, "Can't allocate enough memory\n");
exit(EXIT_FAILURE);
}
size = tmp_size;
buffer = tmp_buffer;
}
buffer[count] = curr_char;
++count;
}
// Here you get buffer with the characters from
// the standard input
fprintf(stderr, "\nNow buffer contains characters:\n");
for (int k = 0; k < count; ++k)
{
fprintf(stderr, "%c", buffer[k]);
}
fprintf(stderr, "\n");
// Todo something with the data
// Free all resources before exist
free(buffer);
exit(EXIT_SUCCESS); }
Compile with -std=c99 option if you use gcc.
Also you can use getline() function which will read from standard input line by line. It will allocate enough memory to store line. Just call it until End-Of-File.
errno = 0;
int read = 0;
char *buffer = NULL;
size_t len = 0;
while ((read = getline(&buffer, &len, stdin)) != -1)
{ // Process line }
if (errno) { // Get error }
// Process later
Note that if you are using getline() you should anyway use dynamic allocated memory. But not for storing characters, rather to store pointers to the strings.

Parsing text in C

I have a file like this:
...
words 13
more words 21
even more words 4
...
(General format is a string of non-digits, then a space, then any number of digits and a newline)
and I'd like to parse every line, putting the words into one field of the structure, and the number into the other. Right now I am using an ugly hack of reading the line while the chars are not numbers, then reading the rest. I believe there's a clearer way.
Edit: You can use pNum-buf to get the length of the alphabetical part of the string, and use strncpy() to copy that into another buffer. Be sure to add a '\0' to the end of the destination buffer. I would insert this code before the pNum++.
int len = pNum-buf;
strncpy(newBuf, buf, len-1);
newBuf[len] = '\0';
You could read the entire line into a buffer and then use:
char *pNum;
if (pNum = strrchr(buf, ' ')) {
pNum++;
}
to get a pointer to the number field.
fscanf(file, "%s %d", word, &value);
This gets the values directly into a string and an integer, and copes with variations in whitespace and numerical formats, etc.
Edit
Ooops, I forgot that you had spaces between the words.
In that case, I'd do the following. (Note that it truncates the original text in 'line')
// Scan to find the last space in the line
char *p = line;
char *lastSpace = null;
while(*p != '\0')
{
if (*p == ' ')
lastSpace = p;
p++;
}
if (lastSpace == null)
return("parse error");
// Replace the last space in the line with a NUL
*lastSpace = '\0';
// Advance past the NUL to the first character of the number field
lastSpace++;
char *word = text;
int number = atoi(lastSpace);
You can solve this using stdlib functions, but the above is likely to be more efficient as you're only searching for the characters you are interested in.
Given the description, I think I'd use a variant of this (now tested) C99 code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
struct word_number
{
char word[128];
long number;
};
int read_word_number(FILE *fp, struct word_number *wnp)
{
char buffer[140];
if (fgets(buffer, sizeof(buffer), fp) == 0)
return EOF;
size_t len = strlen(buffer);
if (buffer[len-1] != '\n') // Error if line too long to fit
return EOF;
buffer[--len] = '\0';
char *num = &buffer[len-1];
while (num > buffer && !isspace((unsigned char)*num))
num--;
if (num == buffer) // No space in input data
return EOF;
char *end;
wnp->number = strtol(num+1, &end, 0);
if (*end != '\0') // Invalid number as last word on line
return EOF;
*num = '\0';
if (num - buffer >= sizeof(wnp->word)) // Non-number part too long
return EOF;
memcpy(wnp->word, buffer, num - buffer);
return(0);
}
int main(void)
{
struct word_number wn;
while (read_word_number(stdin, &wn) != EOF)
printf("Word <<%s>> Number %ld\n", wn.word, wn.number);
return(0);
}
You could improve the error reporting by returning different values for different problems.
You could make it work with dynamically allocated memory for the word portion of the lines.
You could make it work with longer lines than I allow.
You could scan backwards over digits instead of non-spaces - but this allows the user to write "abc 0x123" and the hex value is handled correctly.
You might prefer to ensure there are no digits in the word part; this code does not care.
You could try using strtok() to tokenize each line, and then check whether each token is a number or a word (a fairly trivial check once you have the token string - just look at the first character of the token).
Assuming that the number is immediately followed by '\n'.
you can read each line to chars buffer, use sscanf("%d") on the entire line to get the number, and then calculate the number of chars that this number takes at the end of the text string.
Depending on how complex your strings become you may want to use the PCRE library. At least that way you can compile a perl'ish regular expression to split your lines. It may be overkill though.
Given the description, here's what I'd do: read each line as a single string using fgets() (making sure the target buffer is large enough), then split the line using strtok(). To determine if each token is a word or a number, I'd use strtol() to attempt the conversion and check the error condition. Example:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/**
* Read the next line from the file, splitting the tokens into
* multiple strings and a single integer. Assumes input lines
* never exceed MAX_LINE_LENGTH and each individual string never
* exceeds MAX_STR_SIZE. Otherwise things get a little more
* interesting. Also assumes that the integer is the last
* thing on each line.
*/
int getNextLine(FILE *in, char (*strs)[MAX_STR_SIZE], int *numStrings, int *value)
{
char buffer[MAX_LINE_LENGTH];
int rval = 1;
if (fgets(buffer, buffer, sizeof buffer))
{
char *token = strtok(buffer, " ");
*numStrings = 0;
while (token)
{
char *chk;
*value = (int) strtol(token, &chk, 10);
if (*chk != 0 && *chk != '\n')
{
strcpy(strs[(*numStrings)++], token);
}
token = strtok(NULL, " ");
}
}
else
{
/**
* fgets() hit either EOF or error; either way return 0
*/
rval = 0;
}
return rval;
}
/**
* sample main
*/
int main(void)
{
FILE *input;
char strings[MAX_NUM_STRINGS][MAX_STRING_LENGTH];
int numStrings;
int value;
input = fopen("datafile.txt", "r");
if (input)
{
while (getNextLine(input, &strings, &numStrings, &value))
{
/**
* Do something with strings and value here
*/
}
fclose(input);
}
return 0;
}

How to read a line from the console in C?

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.
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 ;)
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.
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).
How to read a line from the console in C?
Building your own function, is one of the ways that would help you to achieve reading a line from console
I'm using dynamic memory allocation to allocate the required amount of memory required
When we are about to exhaust the allocated memory, we try to double the size of memory
And here I'm using a loop to scan each character of the string one by one using the getchar() function until the user enters '\n' or EOF character
finally we remove any additionally allocated memory before returning the line
//the function to read lines of variable length
char* scan_line(char *line)
{
int ch; // as getchar() returns `int`
long capacity = 0; // capacity of the buffer
long length = 0; // maintains the length of the string
char *temp = NULL; // use additional pointer to perform allocations in order to avoid memory leaks
while ( ((ch = getchar()) != '\n') && (ch != EOF) )
{
if((length + 1) >= capacity)
{
// resetting capacity
if (capacity == 0)
capacity = 2; // some initial fixed length
else
capacity *= 2; // double the size
// try reallocating the memory
if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
}
line[length] = (char) ch; //type casting `int` to `char`
length++;
}
line[length + 1] = '\0'; //inserting null character at the end
// remove additionally allocated memory
if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
return line;
}
Now you could read a full line this way :
char *line = NULL;
line = scan_line(line);
Here's an example program using the scan_line() function :
#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions
char* scan_line(char *line)
{
..........
}
int main(void)
{
char *a = NULL;
a = scan_line(a); //function call to scan the line
printf("%s\n",a); //printing the scanned line
free(a); //don't forget to free the malloc'd pointer
}
sample input :
Twinkle Twinkle little star.... in the sky!
sample output :
Twinkle Twinkle little star.... in the sky!
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;
}
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;
}
On BSD systems and Android you can also use fgetln:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Like so:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
The line is not null terminated and contains \n (or whatever your platform is using) in the end. It becomes invalid after the next I/O operation on stream.
Something like this:
unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer
{
char * strbfr;
int c;
unsigned int i;
i = 0;
strbfr = (char*)malloc(sizeof(char));
if(strbfr==NULL) goto error;
while( (c = getchar()) != '\n' && c != EOF )
{
strbfr[i] = (char)c;
i++;
strbfr = (void*)realloc((void*)strbfr,sizeof(char)*(i+1));
//on realloc error, NULL is returned but original buffer is unchanged
//NOTE: the buffer WILL NOT be NULL terminated since last
//chracter came from console
if(strbfr==NULL) goto error;
}
strbfr[i] = '\0';
*pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer
return i + 1;
error:
*pStrBfr = strbfr;
return i + 1;
}
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.

Resources