CS 240 Lecture 4: More on File I/O
CS 240 Lecture 4: More on File I/O - Detailed Study Notes
Overview and Complete Coverage Check
- Objective: This lecture provides an exhaustive look into File I/O in the C programming language, focusing on safety, advanced parsing, and error detection.
- Review of scanf(): Functions as the inverse of
printf(). It reads formatted input and returns the number of successful conversions. - File I/O Hazards: A "bad" example of File I/O involves declaring a
FILE *, callingfopen(),fprintf(), andfclose()without performing aNULLcheck. This is dangerous and can lead to segfaults. - Proper File I/O Sequence:
- Declare and initialize a file pointer to
NULL(e.g.,FILE *file_ptr = 0). - Callfopen(). - Check if the pointer isNULL. If true, output an error tostderrandreturn 1(or exit). - Perform file operations (e.g.,fprintf()). - Callfclose(). - Reset the pointer toNULLto avoid dangling pointers. - New Functions Introduced:
-
access()-feof()-ferror()-clearerr()
The access() Function: Pre-emptive File Verification
- Purpose: Allows a program to check if a file can be accessed for specific operations (read, write, or existence) BEFORE attempting to open it with
fopen(). This acts as a preventative safety check. - Signature:
int access(char *file_name, int mode) - Requirement: Must include the header
<unistd.h>. - Return Values:
-
0: Success (the file is accessible according to the requested mode). - Non-zero: Failure (the file is either not found or permissions are denied). - Measurement Modes (Constants):
-
R_OK: Checks specifically for read permission. -W_OK: Checks specifically for write permission. -F_OK: Checks for the existence of the file only (does not check permissions). - Critical Distinction: The
access()mode constants (R_OK,W_OK,F_OK) are integers and are entirely different from the string modes used infopen()(e.g.,"r","w","a"). Mixing these up (e.g., passing'r'or"r"toaccess()) is a common trap.
Command-Line Arguments and Main Return Codes
- Usage with main(): The lecture demonstrates using
argcandargvto process filenames passed from the terminal. - Definitions:
-
argc: The "argument count." It represents the number of command-line arguments, including the program's itself. If a user runs./prog file.txt,argcis. -argv[]: An array of strings representing the arguments.argv[0]is always the program name;argv[1]is the first user-provided argument. - Shell Return Codes:
-
return 0frommain(): Signals to the Operating System that the program completed successfully. -return 1(or any non-zero value): Signals to the OS that an error occurred (e.g., improper usage or missing file).
Detecting End of File (EOF) with feof()
- Signature:
int feof(FILE *file_pointer) - Purpose: Determines if the End Of File was reached by the previous read operation.
- Return Value: Returns non-zero if EOF has been triggered; returns
otherwise. - Behavioral Nature:
feof()is reactive, not predictive. It only flags EOF after a read operation has failed to find more data. - The Exam Trap: Checking
feof()before any read leads to errors. If a loop checksfeof()as the condition, and performs the read inside, it may process the last line of the file twice becausefeof()remainsuntil a read actually attempts to cross the EOF boundary and fails. - The Priming Read Pattern: To use
feof()correctly, one must typically perform a "priming read" before the loop, then checkfeof(), then perform subsequent reads at the bottom of the loop body.
The Superior EOF Detection Method: fscanf() Return Values
- Professor's Recommendation: For HW2, students should use the return value of
fscanf()as the primary method to detect EOF. It is cleaner and more robust than relying onfeof(). - Value Interpretation:
-
(Positive integer): Indicates thatvariables were successfully scanned and assigned. -: Indicates a format mismatch or a blank line at the end of the file (no items scanned). -: Indicates that the EOF was reached. - Implementation Logic:
- Check if the return value (
status) is less than the expected count of variables. - A safe rule for stopping is:if (status == 0 || status == -1) break;. - Lecture Quiz Example:
fscanf(fp, "%s - %d", some_buf, &some_int)on input string"Squirrel - 7.5". - Result:fscanf()returns. - Reason:%ssuccessfully captures"Squirrel". The literal string" - "matches. However,%dfails to read7.5(which is a float, not an integer). Since only one variable (some_buf) was successfully assigned, the return value is.
Advanced File Error Handling: ferror() and clearerr()
- ferror():
- Signature:
int ferror(FILE *file_pointer)- Function: Checks for general error conditions on a file stream that are not necessarily EOF (e.g., disk full, hardware failure, I/O errors). - Usage: Callferror()after every write or read operation to verify success. - clearerr():
- Signature:
void clearerr(FILE *file_pointer)- Function: Clears the internal error flag and EOF flag for the given file pointer. - Utility: Necessary if you intend to retry an operation after resolving an error that was previously flagged byferror(). - Example Case Study: A
do-whileloop writing"Hello, world."to a file untilferror()returns non-zero, indicating the disk is likely full.
Advanced Parsing with the %[] Bracket Specifier
- Purpose: Matches a set of characters (a "scan set") instead of just a standard type or whitespace-delimited string.
- Common Patterns:
-
%[0-9]: Matches only numeric digits. -%[A-Z]: Matches only uppercase letters. -%[0-9A-z]: Matches alphanumeric characters. -%[^ ]: Matches everything up to a newline (effectively reading a whole line). -%[^ ]: Reads everything until the newline and then "consumes" (removes) the newline character from the buffer. - Inversion via Caret (^): Placing
^at the start of the set inverts it.%[^0-9]matches anything that is NOT a digit. - Special Character Inclusion:
- To include a literal
]in the set: It must be the first character after the opening[. Example:%[]abc]. - To include a literal-(hyphen) in the set: It must be the last character before the closing]. Example:%[0-9-].
Buffer Overflow: Security Implications and Field Widths
- The Danger: When using
%sor%[]without a limit,scanf()will read characters until it hits a delimiter, regardless of the buffer size. This leads to Buffer Overflows. - Mechanism: C does not perform bounds checking. Writing past the allocated memory of an array (e.g.,
char buf[8]) will corrupt adjacent variables on the stack. - Lecture Demo:
- Memory layout:
[z: 4 bytes] [buf: 8 bytes] [y: 4 bytes]. - Inputting"toolongstring!"(14 characters) into a-byte buffer writes into the space occupied by variablesand`, overwriting their values and causing undefined behavior. - The Fix: Always specify a field width that is exactly
, whereis the buffer size. - Forchar buf[8], use%7sor%7[^ ]. This ensurescharacters plus the null terminator (\0) fit perfectly within the allocatedbytes.
Miscellaneous Preprocessor and Type Details
- #include Syntax:
-
#include "file.h": Searches the current directory first (user-defined headers). -#include <file.h>: Searches system directories (e.g.,/usr/include/). - Preprocessor Action: The preprocessor merges all included file content into a single stream for the compiler.
- The bool Type: Use
#include <stdbool.h>to use thebooltype withtrueandfalseconstants. - Upcoming Topics Preview:
ftell(),fseek(),fgets(),fputs(), andassert().
Study Guide: Visualizations and Summary Table
EOF Detection Strategies
| Method | Mechanism | Recommendation |
|---|---|---|
| feof() | Reactive: checks after previous read. | Moderate; requires "priming read" before loop. |
| fscanf() Return | Checks item count; or indicates stop. | Best Practice (Required for HW2). |
| ferror() | Checks for system/hardware errors (disk full). | Best for write-heavy applications. |
Character Set Scan Rules
- Match Digits:
%[0-9] - Exclude Letters:
%[^A-Za-z] - Include Right Bracket:
%[]...](must be first) - Include Hyphen:
%[...-](must be last)
Active Recall and Practice Problems
Problem: Loop Logic Errors
Identify errors in: char buf[50]; FILE *fp = fopen("data.txt", "r"); while (feof(fp) == 0) { fscanf(fp, "%s", buf); printf("%s\n", buf); }
- Answer 1: Missing
NULLcheck forfpafterfopen(). - Answer 2:
feof()is checked before any read occurs. It will enter the loop even if the file is empty. - Answer 3:
scanf("%s", buf)lacks a field width (should be%49s), risking buffer overflow. - Answer 4: The logic results in the last line being printed twice.
- Answer 5: No
fclose()at the end of the script.
Problem: access() Confusion
Why does if (access("file.txt", 'r') == 0) fail even if the file is readable?
- Answer: The character
'r'(ASCII) is not the correct constant. You must useR_OK, which is the defined integer constant for theaccess()function.