I am attempting to store multiple strings from stdin into a temporary variable.
Each string will be entered following a new line character.
This is my attempt:
#include <stdio.h>
int main() {
for (int i=0; i<10; i++) {
char string;
scanf("%s\n", &string);
printf("%d\n", i);
}
}
The for loop does not run as intended, it only loops twice, seems scanf is interfering with the loop.
stdin:
test
test
stdout:
7631717
Is there a way to store input/each line from stdin dynamically into a char variable without a fixed size/length?
to not be limited to a given size reading a string you can use the modifier 'm' :
char * p;
if (scanf("%ms", &p) == 1) {
...use p;
free(p); /* when stop to be usefull
}
From the man of scanf :
An optional 'm' character. This is used with string conversions (%s, %c, %[), and
relieves the caller of the need to allocate a corresponding buffer to hold the
input: instead, scanf() allocates a buffer of sufficient size, and assigns the
address of this buffer to the corresponding pointer argument, which should be a
pointer to a char * variable (this variable does not need to be initialized before
the call). The caller should subsequently free(3) this buffer when it is no longer
required.
The previous way allows to red a 'word', to read a full line removing spaces (' ', newline, tab ...) at the beginning:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char * p;
if (scanf(" %m[^\n]", &p) == 1) {
printf("'%s'\n", p);
free(p);
}
else
puts("error (EOF...)");
return 0;
}
Compilation and execution:
/tmp % gcc -Wall c.c
/tmp % ./a.out
aze qsd
'aze qsd'
/tmp % ./a.out < /dev/null
error (EOF...)
/tmp %
If you do not have that modifier (not posix ...) to read a full line as it is you can do :
#include <stdio.h>
#include <stdlib.h>
int main()
{
char * p = malloc(1);
size_t len = 0;
int c;
while (((c = getchar()) != '\n') && (c != EOF)) {
p = realloc(p, len+1);
if (p == NULL) {
puts("not enough memory");
return -1;
}
p[len++] = c;
}
p[len] = 0;
printf("'%s'\n", p);
free(p);
return 0;
}
Compilation and execution :
/tmp % gcc -Wall c.c
/tmp % ./a.out
aze qsd
' aze qsd'
/tmp % ./a.out < /dev/null
''
/tmp %
char string; is not a string but a single character. Reading in a string with format %s into a single character is undefined behaviour as soon as you enter at least one character. This is because the string termination character will be appended, too.
Write ...
char string[100];
scanf("%99s", string);
and everything should be fine.
BTW: If you intend to read in complete lines (including white spaces at the beginning, in between and at the end), consider using fgets instead of scanf.
Is there a way to store input/each line from stdin dynamically into a char variable without a fixed size/length?
If willing to going the POSIX route, use getline() to "store input/each line from stdin dynamically".
Lop off the potential trailing new-line if desired
char *line = NULL;
size_t len = 0;
ssize_t nread;
while ((nread = getline(&line, &len, stdin)) != -1) {
if (nread > 0 && line[nread-1] == '\n') line[--nread] = 0;
printf("Retrieved line of length %zu: <%s>\n", nread, line);
}
free(line);
I suspect nread > 0 && line[nread-1] == '\n' can be reduced to line[nread-1] == '\n' as I see not case where nread returns 0.
Related
Hi i am new to C and i am trying to use the Character array type below to captures input from users. How do i prevent or escape numerical characters. I just want only strings to be captured.
char str_input[105];
In have tried
scanf("%[^\n]s",str_input);
scanf("%[^\n]",str_input);
scanf("%[^0-9]",str_input);
scanf("%[A-Zaz-z]",str_input);
str_input = fgetc(stdin);
None of the above worked for me.
Input
2
hacker
Expected Output
Hce akr
int main() {
char *str_input;
size_t bufsize = 108;
size_t characters;
str_input = (char *)malloc(bufsize * sizeof(char));
if (str_input == NULL)
{
perror("Unable to allocate buffer");
exit(1);
}
characters = getline(&str_input,&bufsize,stdin);
printf("%zu characters were read.\n",characters);
int i;
int len = 0;
for (i = 0, len = strlen(str_input); i<=len; i++) {
i%2==0? printf("%c",str_input[i]): 'b';
}
printf(" ");
for (i = 0, len = strlen(str_input); i<=len; i++) {
i%2!=0? printf("%c",str_input[i]): 'b';
}
return 0;
}
Error
solution.c: In function ‘main’:
solution.c:21:5: warning: implicit declaration of function ‘getline’ [-Wimplicit-function-declaration]
characters = getline(&str_input,&bufsize,stdin);
Since your buffer has limited size, then using fgets(3) is fine. fgets() returns NULL on failure to read a line, and appends a newline character at the end of the buffer.
In terms of preventing numerical characters from being in your buffer, you can simply create another buffer, and only add non-numerical characters to it. You could just delete the numerical characters from your original buffer, but this can be a tedious procedure if you are still grasping the basics of C. Another method would be just to read single character input with getchar(3), which would allow you assess each character and simply ignore numbers. THis method is by far the easiest to implement.
Since you asked for an example of using fgets(), here is some example code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define INPUTSIZE 108
int main(void) {
char str_input[INPUTSIZE], characters[INPUTSIZE];
size_t slen, char_count = 0;
printf("Enter input:\n");
if (fgets(str_input, INPUTSIZE, stdin) != NULL) {
/* removing newline from fgets() */
slen = strlen(str_input);
if (slen > 0 && str_input[slen-1] == '\n') {
str_input[slen-1] = '\0';
} else {
fprintf(stderr, "Number of characters entered exceeds buffer size\n");
exit(EXIT_FAILURE);
}
/* checking if string is valid */
if (*str_input == '\0') {
fprintf(stderr, "No input found\n");
exit(EXIT_FAILURE);
}
printf("Buffer: %s\n", str_input);
/* only adding non-numbers */
for (size_t i = 0; str_input[i] != '\0'; i++) {
if (!isdigit(str_input[i])) {
characters[char_count++] = str_input[i];
}
}
/* terminating buffer */
characters[char_count] = '\0';
printf("New buffer without numbers: %s\n", characters);
}
return 0;
}
Example input:
Enter input:
2ttt4y24t4t3t2g
Output:
Buffer: 2ttt4y24t4t3t2g
New buffer without numbers: tttytttg
Update:
You could just use this even simpler approach of ignoring non-number characters:
char str_input[INPUTSIZE];
int ch;
size_t char_count = 0;
while ((ch = getchar()) != EOF && ch != '\n') {
if (!isdigit(ch)) {
if (char_count < sizeof(str_input)) {
str_input[char_count++] = ch;
}
}
}
str_input[char_count] = '\0';
If you're using Linux, I would use the getline() function to get a whole line of text, then verify it. If it is not valid input, I would in a loop ask the user to enter a line of text again and again until you the input is acceptable.
If not using Linux, well, your best bet is probably to reimplement getline(). You can also use fgets() if you find a limited-size buffer acceptable. I don't find limited-size buffers acceptable, so that's why I prefer getline().
getline() is used according to the way explained in its man page: http://man7.org/linux/man-pages/man3/getdelim.3.html
Basically, your loop should be something similar to:
char *buf = NULL;
size_t bufsiz = 0;
while (1)
{
if (getline(&buf, &bufsiz, stdin) < 0)
{
handle_error();
}
if (is_valid(buf))
{
break;
}
printf("Error, please re-enter input\n");
}
use_buffer(buf);
free(buf);
Well that's not possible. Numbers are string too. But you can set loop to look for numbers and print error. like this :
char *str = "ab234cid20kd", *p = str;
while (*p) { // While there are more characters to process...
if (isdigit(*p)) { // Upon finding a digit, ...
printf("Numbers are forbidden");
return 0;
} else {
p++;
}
}
#include <stdio.h>
#include <stdlib.h>
void *salloc(int x){
char **pointer;
int i;
pointer = malloc(sizeof(char)*x);
if(pointer == NULL){
exit(-1);
}
for(i=0; i<x; i++){
pointer[i] = malloc(sizeof(char) * 20);
if(pointer[i] == NULL){
exit(-1);
}
}
return pointer;
}
void Input(int value, char **array){
for(i = 0; i < value; i++){
printf("%d ----\n", i);
fgets(array[i], 20, stdin);
printf("%d ----\n", i);
}
}
int main(int argc, char *argv[]){
char **array;
int value = 2;
array = salloc(value);
Input(value, array);
return 0;
}
The general idea, can be that I miss some syntax.
So I want to read in a string with spaces. If I run this for the value 2, it will print:
0 ----
0 ----
1 ----
"some string"
and it crashes after I press enter.
If I do this with value 1:
it immediately crashes.
However if I replace fgets() with:
scanf("%s", array[i]);
it works (except for the spaces).
So how does fgets() work in 2d-arrays?
Because I get it to work in 1d-arrays. And for some reason I can print 1d-arrays from row 2 when the array only has 2 rows, so it should only be able to print from rows 0 and 1 right?
Here is a demonstrative program that shows how fgets can be used with a 2D array.
#include <stdio.h>
#define N 5
#define M 10
int main( void )
{
char lines[N][M];
size_t n = 0;
while( n < N && fgets( lines[n], sizeof( *lines ), stdin ) != NULL ) ++n;
for ( size_t i = 0; i < n; i++ ) puts( lines[i] );
return 0;
}
If to enter for example
One
Two
Three
Four
Five
then the program output will be the same
One
Two
Three
Four
Five
When you do
pointer = malloc(sizeof(char)*x);
you only allocate x characters (i.e. bytes), not pointers to characters. Change to
pointer = malloc(sizeof(char*)*x);
Without the change, you might go out of bounds and have undefined behavior. And this is exactly what happens in your code, you allocate only two bytes to store two pointers, and a single pointer is either four or eight bytes, so you don't allocate enough memory for even a single pointer.
Undefined behavior is a common cause of crashes, but sometimes it might also seem to work.
Note also when taking input with fgets (or any of the line-oriented input functions), fgets will read up to, and include, the '\n' at the end of each line read. You should perform 2 additional tests/operations. (1) you should test that the last character read by fgets is in fact the '\n' character. If it is not, that will indicate your input was truncated by fgets at the length you specified in the second parameter to fgets, and additional character remain unread for that line. Without this check and some way to handle lines that exceed the specified width, you next call to fgets will read the remaining characters for the current line as your next line of input.
(2) you should remove the newline included by fgets to prevent your strings from containing embedded '\n' characters at the end. (if you are simply parsing numbers from the line and not storing it as a string, this can be handled in several different manners). But, for the general case, you use strlen to locate the end of the string, and then overwrite the '\n' with a nul-terminating character.
In addition to the above, it may make more sense to allocate memory for each individual pointer in array only after a line of data has been read to prevent over-allocating space in your code. Since you are specifying that the allocation size for each string will be 20, a simple character buffer of that size can be used to take input, and after confirming input, you can allocate storage for that line in array. A short example of your function with the checks included and with allocation included in input would be:
#define MAXC 20 /* max chars per read */
...
void Input (int value, char **array)
{
int i = 0;
size_t len = 0;
char buf[MAXC] = {0};
while (i < value && fgets (buf, MAXC, stdin)) {
printf ("%d ----\n", i);
len = strlen (buf); /* get length */
if (len + 1 == MAXC && buf[len-1] != '\n') /* validate read */
fprintf (stderr, "warning: chars exceed MAXC, line[%d]\n", i);
else
buf[--len] = 0; /* strip '\n' */
printf ("%d ----\n", i);
array[i++] = strdup (buf); /* allocate/copy */
}
}
Lastly, why choose void as the function type? Why not return the number of values read into array if values are successfully read into array, or 0 otherwise. This will at least allow some indication of success or failure of your read and provide a way of returning the number of lines allocated back to the calling function.
An example of your code incorporating the adjusted allocation, necessary checks, and useful return types would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 20
void *salloc (int x)
{
char **pointer;
pointer = malloc (sizeof *pointer * x);
if (pointer == NULL)
exit(-1);
return pointer;
}
int input (int value, char **array)
{
int i = 0;
size_t len = 0;
char buf[MAXC] = {0};
while (i < value && fgets (buf, MAXC, stdin)) {
printf ("%d ----\n", i);
len = strlen (buf); /* get length */
if (len + 1 == MAXC && buf[len-1] != '\n') /* validate read */
fprintf (stderr, "warning: chars exceed MAXC, line[%d]\n", i);
else
buf[--len] = 0; /* strip '\n' */
printf ("%d ----\n", i);
array[i++] = strdup (buf); /* allocate/copy */
}
return i;
}
int main (int argc, char *argv[]) {
int i, nlines, value = argc > 1 ? atoi (argv[1]) : 2;
char **array;
array = salloc (value);
if (!(nlines = input (value, array))) /* validate input */
return 1;
for (i = 0; i < nlines; i++) /* print input */
printf (" array[%2d] : %s\n", i, array[i]);
for (i = 0; i < nlines; i++) /* free memory */
free (array[i]);
free (array);
return 0;
}
Test Input File
The following is a test input file where line 1 (the 2nd line) exceedS 20 characters:
$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Example Output
$ ./bin/readarray 10 <../dat/captnjack.txt
0 ----
0 ----
1 ----
warning: chars exceed MAXC, line[1]
1 ----
2 ----
2 ----
3 ----
3 ----
array[ 0] : This is a tale
array[ 1] : Of Captain Jack Spa
array[ 2] : rrow
array[ 3] : A Pirate So Brave
array[ 4] : On the Seven Seas.
Don't forget to use a memory error check program like valgrind to validate your use of the memory you allocate and to insure you have freed it when it is no longer needed. Let me know if you have any additional questions.
I tried to make a program that tells you how many words, lines and characters are in a text file, but the function fopen() fails to open the file. I tried both absolute and relative paths to the text file but I get the same output. Can you please tell me what's wrong?
My compiler is gcc version 4.6.3 (Linux)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 256
void tokenize(const char *filename)
{
FILE *f=NULL;
char line[N],*p;
unsigned long int ch=0,wd=0,ln=0;
int t;
f=fopen(filename,"rt");
if(f==NULL)
{
perror("The following error occurred");
exit(1);
}
fgets(line,N,f);
while(!feof(f))
{
ln++;
p=strtok(line," ");
while(p!=NULL)
{
wd++;
t=strlen(p);
ch+=t;
printf("Word number %lu with length %d: %s\n",wd,t,p);
p=strtok(NULL," ");
}
fgets(line,N,f);
}
printf("%lu lines, %lu words, %lu characters\n",ln,wd,ch);
fclose(f);
}
int main(void)
{
char filename[80];
size_t slen;
printf("Enter filename path:\n");
fgets(filename,80,stdin);
slen = strlen (filename);
if ((slen > 0) && (filename[slen-1] == '\n'))
filename[slen-1] = '\0';
printf("You have entered the following path: %s\n",filename);
tokenize(filename);
return 0;
}
output:
Enter filename path:
input.txt
You have entered the following path: input.txt
The following error occurred: No such file or directory
You've retained the newline character from the input in your filename. You can see this when you echo the filename in your output: notice the blank line.
You'll need to strip off this newline before you pass it to your function. There are a few ways to do this, here's one:
size_t idx = strlen(filename);
if ((idx > 0) && filename[idx - 1] == '\n')
filename[idx - 1] = '\0';
You need to remove the trailing newline character from your string, with something like:
size_t slen = strlen (filename);
if ((slen > 0) && (filename[slen-1] == '\n'))
filename[slen-1] = '\0';
And, while I applaud your use of fgets for user input (since it can be protected from buffer overflow), there's still a couple of edge cases you haven't considered, such as when the line is too long, or the user flags end of input). See here for a more robust solution.
You can declare a function like:
void rmnewline(char *s)
{
int l=strlen(s);
if(l>0 && s[l-1]=='\n')
s[l-1]='\0';
}
and call it before using your char array.
I want to read the name entered by my user using C programmes.
For this I wrote:
char name[20];
printf("Enter name: ");
gets(name);
But using gets is not good, so what is a better way?
You should never use gets (or scanf with an unbounded string size) since that opens you up to buffer overflows. Use the fgets with a stdin handle since it allows you to limit the data that will be placed in your buffer.
Here's a little snippet I use for line input from the user:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
This allows me to set the maximum size, will detect if too much data is entered on the line, and will flush the rest of the line as well so it doesn't affect the next input operation.
You can test it with something like:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
I think the best and safest way to read strings entered by the user is using getline()
Here's an example how to do this:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *buffer = NULL;
int read;
unsigned int len;
read = getline(&buffer, &len, stdin);
if (-1 != read)
puts(buffer);
else
printf("No line read...\n");
printf("Size read: %d\n Len: %d\n", read, len);
free(buffer);
return 0;
}
On a POSIX system, you probably should use getline if it's available.
You also can use Chuck Falconer's public domain ggets function which provides syntax closer to gets but without the problems. (Chuck Falconer's website is no longer available, although archive.org has a copy, and I've made my own page for ggets.)
I found an easy and nice solution:
char*string_acquire(char*s,int size,FILE*stream){
int i;
fgets(s,size,stream);
i=strlen(s)-1;
if(s[i]!='\n') while(getchar()!='\n');
if(s[i]=='\n') s[i]='\0';
return s;
}
it's based on fgets but free from '\n' and stdin extra characters (replacing fflush(stdin) doesn't works on all OS, useful if you have to acquire strings after this).
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. You are allowed to modify the returned line buffer.
Using scanf removing any blank spaces before the string is typed and limiting the amount of characters to be read:
#define SIZE 100
....
char str[SIZE];
scanf(" %99[^\n]", str);
/* Or even you can do it like this */
scanf(" %99[a-zA-Z0-9 ]", str);
If you do not limit the amount of characters to be read with scanf it can be as dangerous as gets
ANSI C unknown maxinum length solution
Just copy from Johannes Schaub's
https://stackoverflow.com/a/314422/895245
Don't forget to free the returned pointer once you're done with it.
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;
}
This code uses malloc to allocate 100 chars. Then it fetches char by char from the user. If the user reaches 101 chars, it doubles the buffer with realloc to 200. When 201 is reached, it doubles again to 400 and so on until memory blows.
The reason we double rather say, just adding 100 extra every time, is that increasing the size of a buffer with realloc can lead to a copy of the old buffer, which is a potentially expensive operation.
Arrays must be contiguous in memory because we wan to be able to random access them efficiently by memory address. Therefore if we had in RAM:
content buffer[0] | buffer[1] | ... | buffer[99] | empty | empty | int i
RAM address 1000 | 1001 | | 1100 | 1101 | 1102 | 1103
we wouldn't be able to just increase the size of buffer, as it would overwrite our int i. So realloc would need to find another location in memory that has 200 free bytes, and then copy the old 100 bytes there and free the 100 old bytes.
By doubling rather than adding, we quickly reach the order of magnitude of the current string size, since exponentials grow really fast, so only a reasonable number of copies is done.
You can use scanf function to read string
scanf("%[^\n]",name);
i don't know about other better options to receive string,
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.