My Problem:
My goal is to read a fixed-length string from the stdin
, store it into a buffer and then immediately proceed the execution of the following code but neither fgets()
nor scanf()
stop to waiting for to get more input, even if the fixed amount of characters to read is fixed -> scanf("%5s",c);
and fgets(b,6,stdin);
.
I have written that example to illustrate the issue:
#include <stdio.h>
#include <string.h>
int main(void)
{
char b[6];
char c[6];
printf("Enter a string of 5 characters (b):\n");
fgets(b, sizeof(b), stdin);
getchar(); // catch the left newline in stdin because in
// b was not enough space.
printf("\nEnter a string of 5 characters (c):\n");
scanf("%5s", c);
//getchar(); // catch the left newline. important if there is any
// read operation thereafter.
printf("\nb: %s\n", b);
printf("c: %s\n", c);
return 0;
}
Execution:
/a.out
Enter a string of 5 characters (b):
hello
Enter a string of 5 characters (c):
world
b: hello
c: world
Both, fgets()
and scanf()
wait for the newline. As a side note: In case of scanf()
with format specifier %s
, any white space would stop the consuming of the directive; In case of scanf()
with format specifier %[
and a negated scanset %[^N]
, the occurence of N
would stop the consuming.
Either way it is waiting for more input, although specified to read only 5 characters.
How do I read a string without waiting for more input?
My Research:
I´ve found a way to achieve what I want but it is a pretty large workaround: I could catch each character separately and use a getch()
derivate for Linux (because I work on Linux and conio.h isn´t a topic here) which are provided in the answers to What is the equivalent to getch() & getche() in Linux? in a for
-loop and thereafter assign the terminating null character to the last element like f.e.:
#include <termios.h>
#include <stdio.h>
static struct termios old, current;
/* Initialize new terminal i/o settings */
void initTermios(int echo)
{
tcgetattr(0, &old); /* grab old terminal i/o settings */
current = old; /* make new settings same as old settings */
current.c_lflag &= ~ICANON; /* disable buffered i/o */
if (echo) {
current.c_lflag |= ECHO; /* set echo mode */
} else {
current.c_lflag &= ~ECHO; /* set no echo mode */
}
tcsetattr(0, TCSANOW, ¤t); /* use these new terminal i/o settings now */
}
/* Restore old terminal i/o settings */
void resetTermios(void)
{
tcsetattr(0, TCSANOW, &old);
}
/* Read 1 character - echo defines echo mode */
char getch_(int echo)
{
char ch;
initTermios(echo);
ch = getchar();
resetTermios();
return ch;
}
/* Read 1 character without echo */
char getch(void)
{
return getch_(0);
}
/* Read 1 character with echo */
char getche(void)
{
return getch_(1);
}
int main(void)
{
char b[6];
int c;
printf("Enter a string of 5 characters (b):\n");
for(int i = 0; i < 5; i++)
{
if((c = getch_(1)) == EOF)
{
if(ferror(stdin))
{
}
else if(feof(stdin))
{
fprintf(stderr,"End of file reached!");
}
}
else
{
b[i] = c;
}
}
b[5] = '\0';
printf("\n\n");
puts(b);
return 0;
}
But this method is a pretty big workaround. Is there a way to achieve that in a simpler way?