Streams#
Need for many channels for IO#
The next problem is not only about debugging but also motivates stderr stream.
Activity 60 (An error in a file processing program)
The following code leads to a segmentation fault (SIGSEGV).
Use the debugger to see what is the problem.
How would you debug it, where would you put your breakpoints?
How would you fix the problem?
#include <stdio.h>
#define LINE_MAX 100
#define FILE_ "data.txt"
int main() {
auto fp = fopen(FILE_, "r");
char line[LINE_MAX];
fgets(line, LINE_MAX, fp);
puts(line);
}
Hint
If you found the error source:
For informative error messages use perror(""). It prints the last occurred error.
Moreover you can also add a message that will be additionally printed. For example, in case a file cannot be found, perror("File cannot be opened") will print to stderr:
File cannot be opened.: No such file or directory
Stream#
- standard streams
preconnected input and output communication channels between a program and its environment when it begins execution.
0 |
1 |
2 |
3 |
... |
n - 1 |
end-of-file |
l |
e |
v |
e |
... |
j |
Three streams#
Fig. 25 a text terminal, the program in execution (process), the streams standard input stdin, standard output stdout, standard error stderr.
Public domain. By Danielpr85 based on Graphviz source of TuukkaH. Source: Wikimedia Commons#
we used
stdinandstdoutbefore.scanf()andprintf()
stderrchannel is useful for separating the actual data from status messages.
stderr's purpose#
Most shell tools use stderr stream for status messages:
$ curl -LH "Accept: text/plain" icanhazdadjoke.com | tr '[:lower:]' '[:upper:]' | sed -E 's/^/π /; s/$/ π€£/'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 51 100 51 0 0 134 0 --:--:-- --:--:-- --:--:-- 134
π WHAT IS RED AND SMELLS LIKE BLUE PAINT?
π€£
π RED PAINT! π€£
In the example above, we see a pipe of three commands. Most terminal shells support pipes. In a pipe, the output of a previous command is used for the next command. In the command above:
curldownloads a dad joke as text.trtranslates lowercase characters to UPPERCASE.sedwraps the string with two emojis.
You see that the last two commands are applied to the actual data β the dad joke, which is sent through stdout, and not to the status data β the download status, which is sent through stderr.
Therefore perror() command in the previous activity uses stderr.
Write error or status messages to stderr, especially if your program outputs actual data to stdout.
Processing the CSV file from the problem.
Appendix#
Checking and resetting stream status#
If reading function return an EOF, this can be due to two reasons:
end of file reached
there has been an error
If either of them has happened, the status must be cleared.
function |
purpose |
|---|---|
|
returns |
|
returns error indicator, e.g., hardware error |
|
clears EOF and error indicators |
Only Use these to understand why a reading or writing operation failed.
#include <stdio.h>
#include <stdlib.h>
#define LINE_MAX 80
#define FILENAME "temperatures.txt"
char line[LINE_MAX];
int main() {
auto fp = fopen(FILENAME, "r");
while (fgets(line, LINE_MAX, fp))
fputs(line, stdout); // puts introduces newlines
if (feof(fp))
fputs("EOF reached.\n", stderr);
else if (ferror(fp)) {
perror(FILENAME " io error");
return EXIT_FAILURE;
} else
puts("New data arrived after EOF");
}
id temperature
0 20
1 21
2 18
3 18
4 19
5 20
When I learned first about feof, I thought feof is good for using it in a control loop as follows:
#include <stdio.h>
#define LINE_MAX 80
char line[LINE_MAX];
int main() {
auto fp = fopen("temperatures.txt", "r");
while (!feof(fp))
puts(fgets(line, LINE_MAX, fp));
}
This is wrong. First we have to attempt reading and then we should use feof. This is an indicator or flag which is set after the first attempt.
feof (and ferror) are probably designed to know the reason why a previous read operation have not succeeded. Read functions like fgets and fgetc() return nullptr and EOF respectively, if nothing can be read. The reason for the EOF can also be an error, which I believe misled me. Both end-of-file and an error can be the reason for not being able read. Only after using feof or ferror can we know the reason.