CoreTechED

Loading

C Program to Read Content of a File: Complete Guide

C Program to Read Content of a File: A Comprehensive Tutorial

Reading a file in C involves opening the file, retrieving its contents, and closing it after use. The C Standard Library provides several functions for reading files depending on the type and format of the data. Understanding file reading is essential for data processing, configuration management, and persistent storage.

This tutorial provides an in-depth exploration of reading files in C: fopen, fgetc, fgets, fscanf, fread, fclose, practical examples, common pitfalls, and best practices for robust file handling.


Conceptual Introduction & Fundamentals

What is File Reading?

File reading is the process of opening a file, retrieving its contents into memory, and closing the file when done. C provides a standard I/O library (<stdio.h>) with functions for reading text and binary files.

Why File Reading Matters

  • Persistent data: Load saved data from disk.
  • Configuration files: Read program settings.
  • Data processing: Analyze logs, CSV files, and structured data.
  • Binary data: Load images, audio, and custom binary formats.
  • Interoperability: Exchange data between programs.

File Reading Functions Overview

FunctionReadsSuitable For
fgetc()Single characterCharacter-by-character processing
fgets()Line/StringReading text files line by line
fscanf()Formatted dataStructured text files
fread()Binary blocksBinary files

Steps to Read a File

  1. Open the file: fopen(filename, mode)
  2. Read the data: Use appropriate function
  3. Close the file: fclose(pointer)

Syntax & Core Mechanics

Opening a File

Syntax:

c

FILE *fopen(const char *filename, const char *mode);

Common modes:

  • "r": Read (text)
  • "rb": Read (binary)
  • "w": Write (text)
  • "a": Append (text)

Example:

c

FILE *fp = fopen("file.txt", "r"); if (fp == NULL) { fprintf(stderr, "Unable to open file\n"); return EXIT_FAILURE; }

Method 1: fgetc() — Character by Character

Reads one character at a time. Returns EOF at end of file.

c

#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp = fopen("file.txt", "r"); int ch; if (fp == NULL) { fprintf(stderr, "Unable to open file.\n"); return EXIT_FAILURE; } while ((ch = fgetc(fp)) != EOF) { putchar(ch); } fclose(fp); return EXIT_SUCCESS; }

Expected output (if file.txt contains “Hello, World!”):

text

Hello, World!

Important: Use int ch, not char ch, to correctly handle EOF.

Method 2: fgets() — Line by Line

Reads one line (up to size - 1 characters) into a buffer.

c

#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp = fopen("file.txt", "r"); char buffer[256]; if (fp == NULL) { fprintf(stderr, "Unable to open file.\n"); return EXIT_FAILURE; } while (fgets(buffer, sizeof(buffer), fp) != NULL) { printf("%s", buffer); } fclose(fp); return EXIT_SUCCESS; }

Expected output (file.txt with multiple lines):

text

Line 1 Line 2 Line 3

Method 3: fscanf() — Formatted Input

Reads formatted data according to a format string.

c

#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp = fopen("data.txt", "r"); char name[50]; int age; if (fp == NULL) { fprintf(stderr, "Unable to open file.\n"); return EXIT_FAILURE; } while (fscanf(fp, "%49s %d", name, &age) == 2) { printf("Name: %s, Age: %d\n", name, age); } fclose(fp); return EXIT_SUCCESS; }

Expected output (data.txt contains “Alice 25\nBob 30\n”):

text

Name: Alice, Age: 25 Name: Bob, Age: 30

Security note: Use width limits like %49s to prevent buffer overflow.

Method 4: fread() — Binary Data

Reads blocks of binary data.

c

#include <stdio.h> #include <stdlib.h> struct Student { int roll; char name[30]; float marks; }; int main(void) { FILE *fp = fopen("students.dat", "rb"); struct Student s; if (fp == NULL) { fprintf(stderr, "Unable to open file.\n"); return EXIT_FAILURE; } while (fread(&s, sizeof(struct Student), 1, fp) == 1) { printf("Roll: %d, Name: %s, Marks: %.2f\n", s.roll, s.name, s.marks); } fclose(fp); return EXIT_SUCCESS; }

Expected output (depends on binary file contents):

text

Roll: 1, Name: Alice, Marks: 85.50 Roll: 2, Name: Bob, Marks: 92.00

Closing a File

Always close files after use.

c

fclose(fp);

Full Practical Code Example with Explanation

This program demonstrates all file reading methods in a comprehensive text and binary file reader.

Program: File Content Reader

c

/** * file_reader.c * Demonstrates reading files using fgetc, fgets, fscanf, and fread. * Includes error handling and multiple reading modes. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define MAX_LINE_LEN 256 #define MAX_NAME_LEN 50 /* Structure for binary data */ typedef struct { int id; char name[MAX_NAME_LEN]; double score; } Record; /* Function prototypes */ void readCharByChar(const char *filename); void readLineByLine(const char *filename); void readFormatted(const char *filename); void readBinary(const char *filename); void demonstrateAllMethods(const char *filename); void createSampleFile(const char *filename); void createBinaryFile(const char *filename); int main(void) { int choice; char buffer[MAX_LINE_LEN]; char filename[MAX_NAME_LEN] = "sample.txt"; printf("=== File Content Reader ===\n"); /* Create sample files for demonstration */ createSampleFile(filename); createBinaryFile("sample.dat"); while (1) { printf("\n--- Menu ---\n"); printf("1. Read Character by Character (fgetc)\n"); printf("2. Read Line by Line (fgets)\n"); printf("3. Read Formatted Data (fscanf)\n"); printf("4. Read Binary Data (fread)\n"); printf("5. Demonstrate All Methods\n"); printf("6. Exit\n"); printf("Enter choice: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return EXIT_FAILURE; } choice = atoi(buffer); switch (choice) { case 1: readCharByChar(filename); break; case 2: readLineByLine(filename); break; case 3: readFormatted(filename); break; case 4: readBinary("sample.dat"); break; case 5: demonstrateAllMethods(filename); break; case 6: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Reads file character by character using fgetc */ void readCharByChar(const char *filename) { printf("\n=== Reading with fgetc ===\n"); FILE *fp = fopen(filename, "r"); if (fp == NULL) { fprintf(stderr, "Error opening %s: %s\n", filename, strerror(errno)); return; } int ch; int charCount = 0; while ((ch = fgetc(fp)) != EOF) { putchar(ch); charCount++; } printf("\n(Total characters: %d)\n", charCount); if (ferror(fp)) { fprintf(stderr, "Error reading file.\n"); } fclose(fp); } /* Reads file line by line using fgets */ void readLineByLine(const char *filename) { printf("\n=== Reading with fgets ===\n"); FILE *fp = fopen(filename, "r"); if (fp == NULL) { fprintf(stderr, "Error opening %s: %s\n", filename, strerror(errno)); return; } char buffer[MAX_LINE_LEN]; int lineCount = 0; while (fgets(buffer, sizeof(buffer), fp) != NULL) { printf("Line %d: %s", ++lineCount, buffer); } printf("(Total lines: %d)\n", lineCount); if (ferror(fp)) { fprintf(stderr, "Error reading file.\n"); } fclose(fp); } /* Reads formatted data using fscanf */ void readFormatted(const char *filename) { printf("\n=== Reading with fscanf ===\n"); FILE *fp = fopen(filename, "r"); if (fp == NULL) { fprintf(stderr, "Error opening %s: %s\n", filename, strerror(errno)); return; } char name[MAX_NAME_LEN]; int age; float score; int recordCount = 0; while (fscanf(fp, "%49s %d %f", name, &age, &score) == 3) { printf("Record %d: Name=%s, Age=%d, Score=%.2f\n", ++recordCount, name, age, score); } printf("(Total records: %d)\n", recordCount); if (ferror(fp)) { fprintf(stderr, "Error reading file.\n"); } fclose(fp); } /* Reads binary data using fread */ void readBinary(const char *filename) { printf("\n=== Reading with fread (Binary) ===\n"); FILE *fp = fopen(filename, "rb"); if (fp == NULL) { fprintf(stderr, "Error opening %s: %s\n", filename, strerror(errno)); return; } Record record; int recordCount = 0; while (fread(&record, sizeof(Record), 1, fp) == 1) { printf("Record %d: ID=%d, Name=%s, Score=%.2f\n", ++recordCount, record.id, record.name, record.score); } printf("(Total records: %d)\n", recordCount); if (ferror(fp)) { fprintf(stderr, "Error reading file.\n"); } fclose(fp); } /* Demonstrates all reading methods */ void demonstrateAllMethods(const char *filename) { readCharByChar(filename); readLineByLine(filename); readFormatted(filename); readBinary("sample.dat"); } /* Creates a sample text file */ void createSampleFile(const char *filename) { FILE *fp = fopen(filename, "w"); if (fp == NULL) { fprintf(stderr, "Error creating %s\n", filename); return; } fprintf(fp, "Alice 25 85.5\n"); fprintf(fp, "Bob 30 92.0\n"); fprintf(fp, "Charlie 22 78.5\n"); fclose(fp); printf("Created sample text file: %s\n", filename); } /* Creates a sample binary file */ void createBinaryFile(const char *filename) { FILE *fp = fopen(filename, "wb"); if (fp == NULL) { fprintf(stderr, "Error creating %s\n", filename); return; } Record records[] = { {1, "Alice", 85.5}, {2, "Bob", 92.0}, {3, "Charlie", 78.5} }; for (int i = 0; i < 3; i++) { fwrite(&records[i], sizeof(Record), 1, fp); } fclose(fp); printf("Created sample binary file: %s\n", filename); }

Explanation of File Reading Methods

  1. fgetc: Reads one character at a time; counts characters.
  2. fgets: Reads line by line; counts lines.
  3. fscanf: Reads formatted records; counts records.
  4. fread: Reads binary structures; counts records.
  5. Error handling: Checks fopen return, uses strerror, checks ferror.

Expected Output (sample run)

text

=== File Content Reader === Created sample text file: sample.txt Created sample binary file: sample.dat --- Menu --- 1. Read Character by Character (fgetc) 2. Read Line by Line (fgets) 3. Read Formatted Data (fscanf) 4. Read Binary Data (fread) 5. Demonstrate All Methods 6. Exit Enter choice: 1 === Reading with fgetc === Alice 25 85.5 Bob 30 92.0 Charlie 22 78.5 (Total characters: 51) --- Menu --- ... Enter choice: 2 === Reading with fgets === Line 1: Alice 25 85.5 Line 2: Bob 30 92.0 Line 3: Charlie 22 78.5 (Total lines: 3) --- Menu --- ... Enter choice: 3 === Reading with fscanf === Record 1: Name=Alice, Age=25, Score=85.50 Record 2: Name=Bob, Age=30, Score=92.00 Record 3: Name=Charlie, Age=22, Score=78.50 (Total records: 3) --- Menu --- ... Enter choice: 4 === Reading with fread (Binary) === Record 1: ID=1, Name=Alice, Score=85.50 Record 2: ID=2, Name=Bob, Score=92.00 Record 3: ID=3, Name=Charlie, Score=78.50 (Total records: 3)

Common Pitfalls, Edge Cases & Memory Considerations

1. Not Checking fopen Return

Using a NULL file pointer causes crashes.

c

FILE *fp = fopen("missing.txt", "r"); fgetc(fp); // BUG: fp is NULL

Solution: Always check if (fp != NULL) after opening.

2. Using char Instead of int for fgetc

char may not represent EOF correctly.

c

char ch; // BUG: should be int while ((ch = fgetc(fp)) != EOF) { ... }

Solution: Use int ch; to correctly handle EOF (which is typically -1).

3. Forgetting to Close Files

Leaving files open wastes resources and may cause data loss.

c

FILE *fp = fopen("file.txt", "r"); /* ... reading ... */ /* Missing fclose(fp); */

Solution: Always fclose files after use.

4. Buffer Overflow with fscanf

Using %s without width limit can overflow the buffer.

c

char name[50]; fscanf(fp, "%s", name); // BUG: no width limit

Solution: Use %49s to limit input.

5. Assuming fgets Always Removes Newline

fgets includes the newline character if space allows.

c

fgets(buffer, sizeof(buffer), fp); /* buffer includes '\n' */

Solution: Remove newline if not needed: buffer[strcspn(buffer, "\n")] = '\0';

6. Not Checking ferror

Read errors may not be detected without checking ferror.

c

while (fgets(buffer, sizeof(buffer), fp) != NULL) { ... } /* Missing if (ferror(fp)) check */

Solution: Check ferror(fp) after the loop.

7. Reading Binary with Text Functions

Using text functions on binary files corrupts data.

c

fgets(buffer, sizeof(buffer), binaryFile); // BUG: binary file

Solution: Use fread for binary files with "rb" mode.

8. Assuming File Exists

Opening a non-existent file in read mode returns NULL.

Solution: Check fopen return and handle gracefully.

9. Not Handling EOF Correctly

EOF is not a character; it’s a special value indicating end of file.

c

while (!feof(fp)) { // Anti-pattern fgets(buffer, sizeof(buffer), fp); printf("%s", buffer); // May print last line twice }

Solution: Use the return value of the read function:

c

while (fgets(buffer, sizeof(buffer), fp) != NULL) { printf("%s", buffer); }

10. Memory Considerations

  • File reading functions use buffers; large buffers consume stack space.
  • fread reads directly into user-provided memory.
  • For very large files, read in chunks rather than all at once.
  • Binary structures may have padding; ensure consistent structure layout.

Summary & Best Practices

Reading files in C involves opening with fopen, reading with appropriate functions (fgetc, fgets, fscanf, fread), and closing with fclose. Proper error handling is essential for robust file operations.

Key Takeaways

  • fgetc: Character by character.
  • fgets: Line by line.
  • fscanf: Formatted data.
  • fread: Binary data.
  • Always check fopen return.
  • Use int for fgetc return value.
  • Always close files with fclose.
  • Check ferror for read errors.

Best Practices

  1. Always check fopen return value before using the file pointer.
  2. Use int for fgetc to handle EOF correctly.
  3. Use width limits in fscanf to prevent buffer overflow.
  4. Use the return value of read functions (not feof) for loop control.
  5. Always close files with fclose after use.
  6. Check ferror after reading to detect errors.
  7. Use the correct mode ("r" for text, "rb" for binary).
  8. Remove trailing newline from fgets if not needed.
  9. Handle missing files gracefully with error messages.
  10. Read large files in chunks rather than all at once.

By mastering file reading in C, you’ll write robust programs that effectively process both text and binary data from files.