IT232 C Programming

C ProgrammingUnit 1011 min read

File Handling in C: Modes, Operations, and Applications

Unit 10 of C Programming covers file handling fundamentals—opening/closing files, reading/writing data, modes (r, w, a, r+), error handling, and practical applications like data storage, logs, and configuration files. Learn with real-world examples (e.g., eSewa transactions, Daraz order logs) and step-by-step code trac

TAKEAWAYS:

  • Files in C are accessed via file pointers (FILE *) using functions like fopen(), fclose(), fprintf(), and fscanf().
  • Modes (r, w, a, r+, w+, a+) determine how files are opened (read/write/append) and whether they truncate existing data.
  • Error handling is critical: always check if fopen() returns NULL to avoid crashes.
  • Binary vs. text mode ("rb", "wb") affects how data (e.g., images, serialized objects) is stored/retrieved.
  • Real-world uses include transaction logs (eSewa), user data storage (Khalti), and configuration files (Pathao’s ride algorithms).
  • Sequential vs. random access: Use fseek() to jump to specific positions in files (e.g., updating a record in a database).

1. Introduction to File Handling

Files are persistent data storage units outside a program’s memory. In C, files are handled using stream-oriented functions from <stdio.h>. Key concepts:

  • Text files: Store characters (e.g., .txt, .csv).
  • Binary files: Store raw data (e.g., .dat, images, executables).
  • File streams: Connections between a program and a file (e.g., stdin, stdout, stderr).

Why Use Files?

  • Persistence: Data survives after the program closes (e.g., saving user profiles in Khalti).
  • Large data: RAM is limited; files store datasets (e.g., NTC’s electricity usage records).
  • Sharing: Multiple programs can access the same file (e.g., Daraz’s order history).

2. File Modes and Their Differences

The mode string in fopen() defines how a file is opened. Common modes:

Mode Description Example Use Case
"r" Open for reading (fails if file doesn’t exist). Reading a config file (e.g., Pathao’s API keys).
"w" Open for writing (creates file; truncates existing data). Overwriting a log file (e.g., Ncell’s call logs).
"a" Open for appending (creates file if missing; adds to end). Adding new transactions to eSewa’s ledger.
"r+" Open for reading and writing (does not truncate). Editing a user’s order history (Daraz).
"w+" Open for reading and writing (truncates existing data). Rewriting a database backup.
"a+" Open for reading and appending. Updating a shared blacklist (e.g., spam emails).

Key Differences: Append ("a") vs. Write ("w")

flowchart TD
    A["File exists?"] -->|"Yes"| B["Mode 'w'"]
    B --> C["Truncates file\nOverwrites data"]
    A -->|"No"| D["Mode 'w'"]
    D --> E["Creates new file"]
    A -->|"Yes"| F["Mode 'a'"]
    F --> G["Appends data\nKeeps existing content"]
    A -->|"No"| H["Mode 'a'"]
    H --> I["Creates new file"]

Example:

  • "w": Writing "Hello" to test.txt (then "World") → File contains only "World".
  • "a": Writing "Hello" (then "World") → File contains "HelloWorld".

3. File Operations: Opening, Closing, and Reading/Writing

Step 1: Opening a File

FILE *file_ptr = fopen("data.txt", "r");
if (file_ptr == NULL) {
    printf("Error opening file!\n");
    exit(1);
}

Visual: File Pointer State


Step 2: Reading/Writing Data

  • Text mode: Use fscanf()/fprintf() (e.g., reading integers as %d).
  • Binary mode: Use fread()/fwrite() (e.g., reading a struct byte-by-byte).

Example: Writing to a File

FILE *fp = fopen("output.txt", "w");
fprintf(fp, "Hello, TU!\n");
fclose(fp);

State After Execution:


Step 3: Closing a File

Always close files to free system resources:

fclose(fp); // Returns 0 on success, EOF on failure.

4. Real-World Applications

Example 1: eSewa Transaction Logs

  • Idea Used: Append mode ("a") to log transactions without losing old data.
  • How It Works:
    • Each payment is appended to transactions.log with timestamp, user ID, and amount.
    • Code snippet:
      FILE *log = fopen("transactions.log", "a");
      fprintf(log, "%s: User %d paid Rs. %d\n", time_str, user_id, amount);
      fclose(log);
      
    • Visual:
      flowchart LR
          A["User pays via eSewa"] --> B["Append to transactions.log"]
          B --> C["File grows: Rs. 500\nRs. 300\n..."]

Example 2: Daraz Order Queue

  • Idea Used: Random access (fseek()) to update order statuses.
  • How It Works:
    • Orders are stored in orders.dat as binary records (structs).
    • When an order is shipped, its status is updated at a specific byte offset.
    • Code snippet:
      typedef struct { int id; char status[20]; } Order;
      Order order;
      FILE *fp = fopen("orders.dat", "r+");
      fseek(fp, id * sizeof(Order), SEEK_SET); // Jump to order #id
      fread(&order, sizeof(Order), 1, fp);
      strcpy(order.status, "Shipped");
      fseek(fp, -sizeof(Order), SEEK_CUR);
      fwrite(&order, sizeof(Order), 1, fp);
      fclose(fp);
      

Example 3: NTC Electricity Billing

  • Idea Used: Text files ("r+" mode) to read/update customer data.
  • How It Works:
    • Customer records (ID, name, units consumed) are stored in billing.csv.
    • When a new reading is taken, the file is opened in "r+", the record is located, and updated.
    • Visual:
      flowchart TD
          A["Read billing.csv"] --> B["Find customer ID 101"]
          B --> C["Update units\nfrom 200 to 250"]
          C --> D["Save changes"]

5. Error Handling in File Operations

Always check for errors:

  1. fopen() failure: Returns NULL if file cannot be opened.
  2. Read/write failures: Check return values of fscanf(), fprintf(), etc.
  3. End-of-file: feof() detects when reading reaches the end.

Example: Safe File Reading

FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    perror("Error opening file");
    return 1;
}
int num;
while (fscanf(fp, "%d", &num) != EOF) {
    printf("Read: %d\n", num);
}
fclose(fp);

6. Binary vs. Text Files

Feature Text Files ("r", "w") Binary Files ("rb", "wb")
Data Format Human-readable (ASCII/Unicode) Raw bytes (e.g., integers as 0x00000064)
Portability Limited (line endings: \n vs. \r\n) High (exact byte representation)
Use Case Logs, CSV data Images, serialized objects, databases
Functions fscanf(), fprintf() fread(), fwrite()

Example: Binary File for NEPSE Stock Data

typedef struct { char symbol[10]; float price; } Stock;
Stock stocks[100];
FILE *fp = fopen("stocks.dat", "wb");
fwrite(stocks, sizeof(Stock), 100, fp); // Write 100 records at once
fclose(fp);

Visual: Binary File Structure



7. Practical Programs (Exam-Style)

Program 1: Copy File Contents

#include <stdio.h>
void copy_file(char *src, char *dest) {
    FILE *fs = fopen(src, "r");
    FILE *fd = fopen(dest, "w");
    char ch;
    if (fs == NULL || fd == NULL) {
        printf("Error opening files!\n");
        return;
    }
    while ((ch = fgetc(fs)) != EOF) {
        fputc(ch, fd);
    }
    fclose(fs);
    fclose(fd);
}

Trace:

Step fs (Source) fd (Destination) Action
1 "Hello" (empty) Open src.txt, dest.txt
2 "ello" "H" Copy 'H'
3 "llo" "He" Copy 'e'
... ... ... ...

Program 2: Reverse a String to File

#include <stdio.h>
#include <string.h>
void reverse_to_file(char *input, char *output) {
    FILE *fp = fopen(output, "w");
    int len = strlen(input);
    for (int i = len - 1; i >= 0; i--) {
        fputc(input[i], fp);
    }
    fclose(fp);
}

Input: "TU" → Output File: "UT". Visual:


Program 3: Odd/Even Number Separator

#include <stdio.h>
void separate_odd_even(int num) {
    FILE *odd = fopen("Odd.dat", "a");
    FILE *even = fopen("Even.dat", "a");
    if (num % 2 == 0) {
        fprintf(even, "%d\n", num);
    } else {
        fprintf(odd, "%d\n", num);
    }
    fclose(odd);
    fclose(even);
}

Trace for Input 5:

File Before After
Odd.dat (empty) 5
Even.dat (empty) (unchanged)

8. Common Pitfalls and Best Practices

  • Forgetting to close files: Leaks resources. Always use fclose().
  • Assuming file existence: Use fopen() checks or access() from <io.h>.
  • Buffer overflows: Use fgets() instead of gets() for strings.
  • Binary vs. text confusion: Use "rb"/"wb" for non-text data (e.g., images).
  • Portability issues: Avoid hardcoding paths (e.g., "C:\\data\\file.txt" → "data/file.txt").

Exam Tip

  1. Memorize modes: Know when to use "a" (append) vs. "w" (overwrite).
  2. Error handling: Always check fopen() and loop conditions (e.g., while (fscanf(...) != EOF)).
  3. Binary files: Use fread()/fwrite() with structs for efficiency (e.g., databases).
  4. File pointers: Understand that FILE * is a stream, not a direct file handle.
  5. Practical questions: Expect programs like:
    • Copying files.
    • Reversing strings to files.
    • Separating data into multiple files (odd/even, valid/invalid records).
  6. Real-world tie-ins: Relate to apps like eSewa (logs), Daraz (order management), or banks (transaction records).

Summary Table: Key Functions

Task Function Example
Open file fopen() FILE *fp = fopen("file.txt", "r");
Close file fclose() fclose(fp);
Read character fgetc() ch = fgetc(fp);
Write character fputc() fputc('A', fp);
Read formatted fscanf() fscanf(fp, "%d", &num);
Write formatted fprintf() fprintf(fp, "%s", str);
Read binary fread() fread(&data, sizeof(data), 1, fp);
Write binary fwrite() fwrite(&data, sizeof(data), 1, fp);
Check EOF feof() while (!feof(fp))
Seek position fseek() fseek(fp, 100, SEEK_SET);

Final Worked Example: Bank Transaction Log

Scenario: A bank writes transactions to transactions.log in append mode. Each entry includes:

  • Timestamp
  • Account ID
  • Amount
  • Type (deposit/withdrawal)

Code:

#include <stdio.h>
#include <time.h>
void log_transaction(char *type, int account_id, float amount) {
    FILE *log = fopen("transactions.log", "a");
    time_t t = time(NULL);
    char *time_str = ctime(&t);
    // Remove newline from ctime output
    time_str[strlen(time_str) - 1] = '\0';
    fprintf(log, "[%s] Account %d: %s Rs. %.2f\n",
            time_str, account_id, type, amount);
    fclose(log);
}

Output File (transactions.log):

[Mon Jun 10 14:30:00 2024] Account 1001: Deposit Rs. 5000.00
[Mon Jun 10 14:35:22 2024] Account 1001: Withdrawal Rs. 2000.00

Visual:

flowchart LR
    A["User deposits Rs. 5000"] --> B["log_transaction(\"Deposit\", 1001, 5000)"]
    B --> C["Append to transactions.log"]
    C --> D["File grows with new entry"]

Based on the TU BITM syllabus for C Programming (IT232), unit 10.

Discussion

Loading…