I would like to have a better understanding of using fgets() and stdin.
The following is my code:
int main()
{
char inputBuff[6];
while(fgets(inputBuff, 6, stdin))
{
printf("%s", inputBuff);
}
return 0;
}
Let's say my input is aaaabbbb and I press Enter. By using a loopcount, I understand that actually the loop will run twice (including the one I input aaaabbbb) before my next input.
Loop 1: After I have typed in the characters, aaaabbbb\n will be stored in the buffer of stdin file stream. And fgets() is going to retrieve a specific number of data from the file stream and put them in inputBuff. In this case, it will retrieve 5 (6 - 1) characters at a time. So that when fgets() has already run once, inputBuff will store aaaab, and then be printed.
Loop 2: Then, since bbb\n are left in the file stream, fgets() will execute for the second time so that inputBuff contains bbb\n, and then be printed.
Loop 3: The program will ask for my input (the 2nd time) as the file stream has reached the end (EOF).
Question: It seems that fgets() will only ask for my keyboard input after stdin stream has no data left in buffer (EOF). I am just wondering why couldn't I use keyboard to input anything in loop 2, and fgets() just keep on retrieving 5 characters from stdin stream and left the excess data in the file stream for next time retrieval. Do I have any misunderstanding about stdin or fgets()? Thank you for your time!
The behavior of your program is somewhat more subtle than you expect:
fgets(inputBuff, 6, stdin) reads at most 5 bytes from stdin and stops reading when it gets a newline character, which is stored into the destination array.
Hence as you correctly diagnose, the first call reads the 5 bytes aaab and prints them and the second call reads 4 bytes bbb\n and prints them, then the third call gets an empty input stream and waits for user input.
The tricky part is how stdin gets input from the user, also known as console input.
Both console input and stdin are usually line buffered by default, so you can type a complete line of input regardless of the size of the buffer passed to fgets(). Yet if you can set stdin as unbuffered and console input as uncooked, the first fgets() would indeed read the first 5 bytes as soon as you type them.
Console input is an intricate subject. Here is an in depth article about its inner workings: https://www.linusakesson.net/programming/tty/
Everything is there in manual page of fgets() whatever you are asking. Just need to read it properly, It says
char *fgets(char *s, int size, FILE *stream);
fgets() reads in at most one less than sizecharacters
from stream and stores them into the buffer pointed to by s. Reading
stops after an EOF or a newline. If a newline is read, it is
stored into the buffer. A terminating null byte (aq\0aq) is stored
after the last character in the buffer.
If input is aaaabbbb and in fgets() second argument you specified size as 6 i.e it will read one less 5 character and terminating \0 will be added so first time inputBuff holds aaaab and since still EOF or \n didn't occur so next time inputBuff holds bbb\n as new line also get stored at last.
Also you should check the return type of fgets() and check if \n occurs then break the loop. For e.g
char *ptr = NULL;
while( (ptr = fgets(inputBuff, 6, stdin))!= NULL){
if(*ptr == '\n')
break;
printf("%s", inputBuff);
}
fgets() does only read until either '\n' or EOF. Everything after that will be left in stdin and therefore be read when you call fgets() again. You can however remove the excess chars from stdin by for example using getc() until you reach '\0'. You might want to look at the manpages for that.
Related
In this code:
#include<stdio.h>
int main()
{
int i,p=0;
while(i!=EOF)
{
i=getchar();
putchar(i);
printf("\n");
}
return 0;
}
When I enter hello as input in one go, the output is h then in the next line e and so on. But when h is printed then before printing e why getchar() doesn't take pause to take input from me just like it did in the first time?
getchar() returns either any successfully read character from stdin or some error, so which function is demanding terminal input and then sending it to stdin?
Input from a terminal is generally buffered. This means it is held in memory waiting for your program to read it.
This buffer is performed by multiple pieces of software. The software that is actually reading your input in the terminal window generally accumulates characters you type until you press enter or press certain other keys or combinations that end the current input. Then the line that has been read is made available to your program.
Inside your program, the C standard library, of which getchar is a part, reads the data that has been sent to it and holds it in a buffer of its own. The getchar routine reads the next character from this buffer. (If the buffer is empty when getchar wants another character, getchar will block, waiting for new data to arrive from the terminal software.)
It's because of the loop condition. You are continuing to loop until EOF is received. When you type "hello", it works exactly as you expect except STDIN has more characters in the buffer and none of them are EOF. The program prints out "h", then a newline, and goes back to check the loop condition. EOF has not been found, so then it gets the next character from STDIN (which you have already provided) and the cycle repeats.
If you remove the loop it will only print one character.
I am relatively new to C programming, but, from what I understand, fscanf skips any whitespace when scanning input for every type beside characters. What other means do I have to scan integers while keeping any newline character that may be attached to them within the file (as I actually want to do something with these newline characters)?
You can use fgets in the following format:
char *fgets(char *s, int size, FILE *stream);
and as it's man page says:
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is
stored after the last character in the buffer.
It will read a newline character, however it will stop reading after that and terminate with the null character.
You could use fcanf(file,"%[^\n]") which takes everything up to user hit enter.
as a Linux system programming exercise I've written my own version of the tree command, which is to read from stdin and write to stdout using only the basic read() and write() C library functions. I've done it so that when an asterisk (*) is entered, the program is terminated. I have managed to get it to work properly, my problem is that I don't really understand why it works the way it does. What confuses me is the buffer. First of all, here is the code portion in question:
char buf[1];
...
do {
read(STDIN_FILENO, buf, 1);
if( buf[0] == '*') break;
write(STDOUT_FILENO, buf, 1);
} while( buf[0] != '*');
...
My idea was to read from stdin char by char, thereby storing the char in buf, check if it was an asterisk, then write the char from buf to stdout.
The behaviour is the following: I type a string of any number of chars, press ENTER, that string gets output to stdout, at which point I can type a new char string. If the string ends with an asterisk, the string is output up until the asterisk, then the program is terminated.
My problems are:
1) buf is sopposed to contain only one char. How is it possible that I enter any number of chars und upon pressing ENTER all of them are output to stdout? I would expect one char at a time to be output, or only the last one. How does a one-char buffer store all of those chars? Or do many one-char buffers get created? By whom?
2) What is so special about the newline character that prompts the string to be output? Why is it not just another char within the string? Is it just a matter of definition within the function read()?
Thank you for any help in understanding the working of the buffer!
This is based upon the way the IO calls - read and write will work on most OS's.
You are reading only 1 byte, so while you are typing, stuff will be held by an io buffer (not yours), until your loop reads it. Since you have no sleeps, it will be reading, or waiting to read faster than you can humanly type.
Also as R Sahu suggests - the input buffer may not be presented to your program until you press enter on the console you are typing at. This depends on the console and its config - but most will buffer lines and wait for enter too. This would be different if you were piping into stdin.
The last parameter to read, the '1', is what instructs it to read one byte here.
The second part is that your output is also buffered, and newline is commonly used by console output buffers to flush and show the line. Until that case, it is being written by your code to that output buffer. If you do not want this behaviour, then an fflush call after the write should output character by character instead.
When you type in your input at a console, the input characters are not immediately fed to stdin. After you press the Enter button, the entire line you typed, including the newline character, are is fed to stdin by the run time environment.
#define MAX_COMMAND_LEN 32
char command[MAX_COMMAND_LEN];
while (1) {
if (fgets(command, MAX_COMMAND_LEN, stdin) == NULL) {
perror("Error: standard function fgets has failed\n");
break;
}
if (command[strlen(command) -1] != '\n') {
printf("Error: command length must be less than or equal to 30 characters\n");
continue;
}
else {
printf("Error: command not found\n");
}
}
quit();
I have couple of problems which I'm not able to handle:
When I press Enter, it stops the loop and doesn't print the command not found message.
When I enter a command with a size bigger than 30 characters it prints both the command not found and the command length must be less than or equal to 30 characters messages.
When I enter a 64 size command it prints twice the 30-length message.
I believe it divides the input to 30-length segments and input each one of them, how do I overcome it? I tried to flush stdin, it does not work. I want to get rid of the rest of the input. How do I overcome all these problems?
For your second problem, it's because fgets fetches the 31 (MAX_COMMAND_LEN, minus space for the terminating '\0' character) first characters, you notice it's no newline and the next time around the loop fgets fetches the remaining characters.
When I enter a command with a size bigger than 30 characters it prints both the 'command not found' and the 'command length must be less than or equal to 30 characters' messages.
fgets reads a maximum of MAX_COMMAND_LEN - 1 characters as it leaves room for a '\0'.
This is why for any message of more than 30 character, the first 31 characters that fgets reads don't contain a '\n' and so the 30-length message is displayed.
As the second part of the command has a '\n' in the end, the command not found is also printed.
When I enter a 64 size command it prints twice the 30-length message.
fgets is called 3 times for this command. The first 31-length chunk is read, then the second 31-length chunk is read, and then the remaining characters. Both 31-length chunks don't contain a '\n' character and therefore the 30-length message is displayed twice.
You may have misunderstood how fgets actually works:
char * fgets ( char * str, int num, FILE * stream );
Reads characters from stream and stores them as a C string into str
until (num-1) characters have been read or either a newline or the
End-of-File is reached, whichever comes first. A newline character
makes fgets stop reading, but it is considered a valid character and
therefore it is included in the string copied to str. A null
character is automatically appended in str after the characters read
to signal the end of the C string.
So,
when you press "Enter" - it inputs newline and that is not exceptional case in your code.
when you input a string bigger than 30 characters fget reads it for multiple times.
same with the 60 characters - newline is NOT the last character of the string, thus you get an error twice.
For question (i), sorry, I don't know why, because my program gives the correct output.
For question (ii), you give the second argument of fgets is 32, the function will read at most 32 characters including the '\n' and '\0'; what's left is still in the stdin buffer, and when your program continues after printing the error, the fgets will read the leftover characters in the stdin buffer until it reads '\n'.
If you want to flush the stdin, you need fpurge(stdin) function to purge the stdin buffer.
I am having a program where
fscanf(fp,"%[^\n]s",line);
is used for reading a line.
If I put in a while loop,
while(!feof(fp))
fscanf(fp,"%[^\n]s",line);
the above code works for first line and for the rest, I am getting
line as NULL. ( line = "" )
My file contains many lines even many blank lines. How can I make the above code work?
First, the conversion specifier would be %[^\n] (no s on the end).
Secondly, you don't want to use the %[ conversion specifier without an explicit size; otherwise you run the risk of a buffer overflow:
char line[132];
...
fscanf(fp, "%131[^\n]", line);
Third, this will leave the newline in the input stream, potentially fouling up the next read.
Finally, you don't want to use feof as your loop condition, since it won't return true until after you try to read past EOF, causing your loop to execute one too many times.
Frankly, I think the better option is to use fgets(); it will read everything up to and including the next newline or one less than the specified size. IOW, if line is sized to hold 20 characters and the input line has 80 characters (including the newline), fgets will read 19 characters and append the 0 terminator into line. If the input line is 10 characters, it will read the whole input line into line (including the newline).
fgets will return NULL on EOF or error, so you should structure your loop as
while (fgets(line, sizeof line, fp))
{
// do something with line
}
if (feof(fp))
// hit end of file
else
// error on read.
I'm pretty sure that \n is not allowed within a scanset. It would be helpful if you state what you're trying to code there.
If you want to read in entire lines, I'd strongly suggest you go for the fgets() library routine. With fgets() you're able to state, how much characters to read in at max, so you're able to avoid buffer overflows.