class: center, middle # CMPE 30: Lecture 8 Standard I/O --- # Review: Where is the bug? Goal: `year` holds the number 1985. ```c char *year_str = "1985"; int year = (int)year_str; ``` 1. Nothing --- the cast parses the string 1. It converts the *address*, not the text --- use `strtol` 1. `year_str` must be a `char[]` 1. `(int)` should be `(long)` 1. Ben got this wrong --- # Learning Objectives - `scanf` input and the `&` rule - `stdin`, `stdout`, `stderr` - `fprintf` / `fscanf` - `fopen` / `fclose` and mode strings - `sprintf`, `sscanf`, and scan sets - Binary I/O: `fwrite` / `fread` - Reading lines: `fgets` - Buffering and `fflush` --- # stdio.h Replaces iostream - Everything flows through `FILE *` --- an opaque stream handle - `printf` / `scanf`: formatted text - `fopen` / `fclose`: files - `fread` / `fwrite`: raw bytes - Last new-material lecture --- this completes your C toolbox --- # scanf: Give It Addresses .lc[ ```c int year; printf("Enter a year: "); scanf("%d", &year); printf("You entered: %d\n", year); ``` ] .rc[ - `&year` = "store the result *here*" - Forgetting `&` compiles, then crashes or prints garbage - Arrays need no `&` --- they decay to pointers ] --- # scanf Format Gotchas ```c char name[50]; double gpa; scanf("%49s %lf", name, &gpa); ``` - `%lf` for `double` --- `printf` uses `%f` - `%s` reads **one word**, no bounds check - Always width-limit: `%49s` = 49 chars + `'\0'` - Whole lines: prefer `fgets` --- # Three Streams, Always Open | Stream | Purpose | C++ | |:---|:---|:---| | `stdin` | input (keyboard) | `std::cin` | | `stdout` | output (screen) | `std::cout` | | `stderr` | errors (screen) | `std::cerr` | - `printf(...)` is `fprintf(stdout, ...)` --- # stderr and Redirection ```c fprintf(stderr, "Error: file not found\n"); ``` - `./program > out.txt` captures `stdout` only - Errors still reach the screen - `./program 2> err.txt` redirects `stderr` - `stderr` is unbuffered --- errors appear immediately --- # fscanf: Reading Structured Text .lc[ ```c FILE *f = fopen("scores.txt", "r"); char name[50]; int score; while (fscanf(f, "%49s %d", name, &score) == 2) { printf("%s scored %d\n", name, score); } fclose(f); ``` ] .rc[ - Same format strings, plus a `FILE *` first - Returns the number of items read - `== 2` --- got both, keep going - Stops cleanly at end of file ] --- # fopen and fclose ```c FILE *f = fopen("setlist.txt", "w"); if (f == NULL) { fprintf(stderr, "cannot open\n"); return 1; } fprintf(f, "Eye of the Tiger\n"); fprintf(f, "Year: %d\n", 1982); fclose(f); ``` - `fclose` flushes buffered output and releases the stream **Trap:** `fopen` returns `NULL` on failure --- always check first. --- # Mode Strings | Mode | Meaning | |:---|:---| | `"r"` | read (file must exist) | | `"w"` | write (create or truncate) | | `"a"` | append (create or append) | | `"r+"` `"w+"` `"a+"` | read + write variants | - Add `b` for binary: `"rb"`, `"wb"` - Unix: no difference; Windows: text mode translates `\r\n` --- # sprintf and sscanf .lc[ ```c char buf[100]; sprintf(buf, "Track %02d: %s", 2, "Whip It"); // "Track 02: Whip It" int track; char title[50]; sscanf(buf, "Track %d: %49[^\n]", &track, title); // track=2, title="Whip It" ``` ] .rc[ - `sprintf` formats **into a buffer** - `sscanf` parses **from a string** - `%[aeiou]` = scan set: only the listed chars - Leading `^` negates: `%79[^\n]` reads up to a newline - POSIX `%m` mallocs the buffer (glibc only --- you must `free`) ] --- # snprintf: The Safe One ```c char small[15]; snprintf(small, sizeof(small), "Jenny: %d", 8675309); // "Jenny: 8675309" --- 14 chars + '\0' ``` - **Trap:** `sprintf` overflows like `strcpy` - `snprintf` never writes more than `size` bytes, `'\0'` included - **Tip:** POSIX `asprintf(&msg, ...)` mallocs an exact-size string --- you must `free` it --- # fwrite and fread .lc[ ```c int tracks[] = {4, 8, 15, 16}; FILE *f = fopen("tracks.bin", "wb"); fwrite(tracks, sizeof(int), 4, f); fclose(f); f = fopen("tracks.bin", "rb"); int back[4]; fread(back, sizeof(int), 4, f); fclose(f); ``` ] .rc[ - Args: pointer, element size, count, stream - Raw bytes --- no formatting - File is exactly 16 bytes - Return value = elements moved --- check `fread`'s ] --- # Reading Lines: fgets ```c char *fgets(char *s, int size, FILE *stream); ``` ```c char line[80]; while (fgets(line, sizeof(line), f) != NULL) { printf("%s", line); // '\n' included } ``` - Stops at `size - 1` chars, a newline (kept), or EOF - Always null-terminates on success; `NULL` at EOF - Safer than `scanf` --- it respects the buffer size --- # Buffering: Three Modes - **Full:** flush when the buffer fills (files) - **Line:** flush at each `\n` (`stdout` at a terminal) - **Unbuffered:** immediately (`stderr`) Output waits in a buffer --- `stdio` does not hit the device on every call. --- # fflush ```c printf("Working..."); fflush(stdout); // appear NOW // ... long computation ... printf(" done!\n"); ``` - No `\n`, no flush --- without `fflush` nothing shows until the end - **Trap:** redirected `stdout` is *fully* buffered --- even `\n` no longer flushes --- # Live Coding: Mixtape Files .lc[ ```c FILE *f = fopen("mixtape.txt", "w"); fprintf(f, "Devo %d\n", 1980); // ... Survivor, Prince ... fclose(f); f = fopen("mixtape.txt", "r"); while (fscanf(f, "%49s %d", artist, &year) == 2) { printf("%s: %d\n", artist, year); } fclose(f); ``` ] .rc[ ```c char small[15]; snprintf(small, sizeof(small), "Jenny: %d", 8675309); ``` - Write it, read it back - Then a binary round-trip with `fwrite`/`fread` - Finish with `snprintf` ] --- # Live Coding: Experiments - `cat mixtape.txt`; `xxd tracks.bin` - Change `"w"` to `"a"`, run twice --- the mixtape doubles - Drop an `&`, read the warning - Redirect with `>` and watch the buffering change - Shrink to `char small[8]` --- truncates safely to `Jenny: ` - `-Wall` warns: `'%d' directive output truncated` --- # What does this print? ```c char buf[20]; snprintf(buf, sizeof(buf), "Track %02d", 7); printf("%zu\n", strlen(buf)); ``` 1. 6 1. 7 1. 8 1. 10 1. Ben got this wrong --- # Run at a terminal: what appears? ```c printf("Working..."); sleep(2); printf(" done!\n"); ``` 1. `Working...` now, ` done!` 2 s later 1. Nothing for 2 s, then all at once 1. `Working...` at 2 s, ` done!` at 4 s 1. Deadlock --- missing `fflush` is UB 1. Ben got this wrong --- # Where does the stderr line go? ```c // run as: ./program > output.txt fprintf(stderr, "boom\n"); ``` 1. Into `output.txt` 1. To the screen 1. Nowhere --- it is discarded 1. Both the screen and `output.txt` 1. Ben got this wrong --- # Key Points - `scanf` needs `&` for scalars; arrays decay - `fopen` returns `NULL` --- check it; `fclose` flushes - Add `"b"` to the mode for binary files - `fwrite`/`fread`: pointer, size, count, stream - `fgets` for whole lines --- it respects the buffer size - `snprintf`, not `sprintf` - Terminal = line buffered; redirected = fully buffered; `fflush` to force **Read (optional):** chapters 11 (Low-Level I/O) and 12 (Odds and Ends) of *Gorgo C for C++ Programmers* --- for the curious. Prep for the final with the study guides and the lecture quizzes.