Skip to main content

Chapter Summary

This chapter introduced file input/output (I/O) operations in C, enabling programs to read from and write to files for persistent data storage. File operations are essential for working with large datasets that cannot be interactively entered and for preserving program output beyond a single execution.

Key Concepts

File Pointers and File Handles

  • File Pointer: A variable of type FILE* that serves as a handle to an open file
  • Declaration Syntax: FILE* variableName;
  • Purpose: Allows a program to reference and interact with a specific file

Opening Files with fopen()

  • Function Signature: FILE* fopen(const char* filename, const char* mode);
  • Parameters:
    • filename: A string containing the path to the file
    • mode: A string specifying how the file should be accessed
  • Return Value: A FILE* pointer if successful; NULL if the operation fails
  • File Access Modes:
    • "r" (Read): Opens an existing file for reading; file must exist
    • "w" (Write): Opens a file for writing; truncates (empties) existing file or creates new file
    • "a" (Append): Opens a file for writing at the end; preserves existing content or creates new file
  • Error Checking: Always verify that fopen() succeeded by checking if the returned pointer is not NULL before attempting file operations

Reading from Files

fgets() Function

  • Function Signature: char* fgets(char* str, int size, FILE* fileHandle);
  • Purpose: Reads an entire line from a file into a string buffer
  • Parameters:
    • str: Character array where the line will be stored
    • size: Maximum number of characters to read (including null terminator)
    • fileHandle: File pointer to read from
  • Return Value: Pointer to the string if successful; NULL if end-of-file is reached
  • Behavior: Includes the newline character (\n) in the string if present
  • Use Case: Ideal for reading unstructured text data line-by-line

fscanf() Function

  • Function Signature: int fscanf(FILE* fileHandle, const char* format, ...);
  • Purpose: Reads formatted data from a file using format specifiers
  • Parameters:
    • fileHandle: File pointer to read from
    • format: Format string with specifiers and optional regular expressions
    • Additional arguments: Pointers to variables where data will be stored
  • Return Value: Number of successfully read items; useful for detecting end-of-file or read failures
  • Regular Expressions: Format codes using [] notation to match specific character patterns
    • %[^\n]: Matches any string up to but not including a newline
    • %[^,]: Matches any string up to but not including a comma
  • Use Case: Ideal for reading structured data with known format patterns

Writing to Files

fprintf() Function

  • Function Signature: int fprintf(FILE* fileHandle, const char* format, ...);
  • Purpose: Writes formatted data to a file, similar to printf() but targets a file
  • Parameters:
    • fileHandle: File pointer to write to
    • format: Format string with specifiers (same as printf())
    • Additional arguments: Values to be formatted and written
  • Behavior: Works identically to printf() except output goes to a file instead of the screen
  • Use Case: Writing structured or formatted data to files

Closing Files with fclose()

  • Function Signature: int fclose(FILE* fileHandle);
  • Purpose: Closes a file and releases associated system resources
  • Parameters: fileHandle - File pointer to close
  • Return Value: 0 if successful; non-zero if an error occurs
  • Critical Importance:
    • Flushes buffered data to disk (ensures data is actually written)
    • Frees system resources
    • Prevents accidental further access to the file
  • Best Practice: Always close files when finished; failure to close can result in data loss and resource leaks

Common File I/O Patterns

Pattern 1: Read Entire File Line-by-Line

FILE *fp = fopen("file.txt", "r");
int rc = 0;
if (fp == NULL) {
printf("Error opening file\n");
rc = 1;
}
char line[256];
while (fgets(line, sizeof(line), fp) != NULL) {
// Process line
}
fclose(fp);

Pattern 2: Read Entire File Character-by-Character

FILE *fp = fopen("file.txt", "r");
int rc = 0;
if (fp == NULL) {
printf("Error opening file\n");
rc = 1;
}
int ch;
while ((ch = fgetc(fp)) != EOF) {
// Process character
}
fclose(fp);

Pattern 3: Read Structured Data

FILE *fp = fopen("data.txt", "r");
int rc = 0;
if (fp == NULL) {
printf("Error opening file\n");
rc = 1;
}
int id;
char name[50];
while (fscanf(fp, "%d %s", &id, name) == 2) {
// Process record
}
fclose(fp);

Working with Structured Data Files

  • Struct Definition: Create structs to represent the logical structure of records in a file
  • Array of Structs: Use arrays of structs to store multiple records read from a file
  • Format Specification: Design fscanf() format strings to match the exact layout of data in the file
  • Lazy Evaluation: When using && in loop conditions, place bounds checking first to prevent out-of-bounds array access before attempting file operations
  • Return Values: Functions that read files should return the number of records successfully read to indicate completion and success

End-of-File Detection

  • fgets(): Returns NULL when end-of-file is reached
  • fscanf(): Returns the number of items successfully read; returns fewer items than expected at end-of-file
  • feof(): Function that indicates whether a preceding input operation encountered the end-of-file marker
  • Proper Detection: Use the appropriate method based on which function is being used for file reading