File System and I/O Operations

Chapter 17: File System and I/O Operations

In Unix-like systems (and by extension C), "everything is a file." Whether you are writing to a hard drive, a keyboard, or a network socket, the C standard library provides a unified interface for Input/Output (I/O).

I. Streams and the FILE Pointer

C uses Streams to handle I/O. A stream is an abstraction that represents a flow of data. The standard library uses the FILE * type to manage these streams.

Standard Streams

  • stdin: Standard Input (Keyboard)
  • stdout: Standard Output (Console)
  • stderr: Standard Error (Console, for error messages)

II. Working with Files

The basic lifecycle of file I/O involves: Open -> Process -> Close.

#include <stdio.h>

int main() {
    FILE *fptr;
    fptr = fopen("data.txt", "w"); // Open for writing

    if (fptr == NULL) {
        perror("Error opening file");
        return 1;
    }

    fprintf(fptr, "Hello File System!\n");
    fclose(fptr); // Crucial to flush buffers and release handles
    return 0;
}

Common Modes for fopen

  • "r": Read (fails if file doesn't exist).
  • "w": Write (creates file or overwrites).
  • "a": Append (adds to end of file).
  • "r+": Read and Write.

III. Formatted vs. Character I/O

  1. Formatted: fprintf and fscanf. Use these for text data where you want to read/write numbers and strings.
  2. Character: fgetc and fputc. Process one character at a time.
  3. Line: fgets and fputs. Read/write an entire line. Avoid gets() as it is highly insecure.

IV. Binary I/O: fread and fwrite

When working with images, audio, or database records, you don't want to translate data to text. You want to write raw bytes.

struct Record { int id; float value; };
struct Record r = {1, 99.5f};

// Write a whole struct directly to disk
fwrite(&r, sizeof(struct Record), 1, fptr);

ProgramBuffer PoolOS KernelPage CacheDisk

V. Random Access: fseek and ftell

You can move the "file pointer" to any byte position in the file.

  • fseek(fp, offset, origin): Move to a position. SEEK_SET (start), SEEK_CUR (current), SEEK_END (end).
  • ftell(fp): Get the current byte position.

VI. Error Handling: feof and ferror

Always check for the end-of-file (feof) and potential disk errors (ferror). Using perror prints a human-readable error message based on the global errno variable.