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).

  1. Use the debugger to see what is the problem.

    How would you debug it, where would you put your breakpoints?

  2. 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.

Table 3 each file is a stream of bytes until end-of-file (EOF). Example for a file that contains the word leverpostej#

0

1

2

3

...

n - 1

end-of-file

l

e

v

e

...

j

Three streams#

https://upload.wikimedia.org/wikipedia/commons/7/70/Stdstreams-notitle.svg

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 stdin and stdout before.

    • scanf() and printf()

  • stderr channel 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:

  1. curl downloads a dad joke as text.

  2. tr translates lowercase characters to UPPERCASE.

  3. sed wraps 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.

πŸ₯‘ Takeaway

Write error or status messages to stderr, especially if your program outputs actual data to stdout.

⚑ Live programming

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:

  1. end of file reached

  2. there has been an error

If either of them has happened, the status must be cleared.

function

purpose

int feof(FILE *stream)

returns EOF indicator, i.e., non-zero if EOF reached

int ferror(FILE *stream)

returns error indicator, e.g., hardware error

int clearerr(FILE *stream)

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
πŸ•³οΈ Pitfall

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.

Details: Why is while(!feof(file)) always wrong?