File Handling in C – Complete Guide with Examples

File handling in C is done through the standard I/O library (<stdio.h>). The library gives you a small set of functions — fopen, fclose, fread, fwrite, fprintf, fscanf, fgets, fseek — that work the same way on every platform. This guide walks through each operation with a complete, compilable example.

Opening and Closing a File

Every file operation starts with fopen, which returns a FILE *. Always check for NULL and always close with fclose:

#include <stdio.h>

int main(void)
{
    FILE *fp = fopen("hello.txt", "w");
    if (fp == NULL) {
        perror("fopen");
        return 1;
    }

    fprintf(fp, "Hello, file!\n");

    fclose(fp);
    return 0;
}

Mode strings control how the file is opened:

Mode Meaning File exists? File missing?
"r" Read only Opens Returns NULL
"w" Write only Truncates to zero Creates
"a" Append Writes at end Creates
"r+" Read + write Opens Returns NULL
"w+" Read + write Truncates to zero Creates
"rb" Binary read Opens Returns NULL
"wb" Binary write Truncates to zero Creates

Writing Text to a File

fprintf works exactly like printf but sends output to a file:

#include <stdio.h>

int main(void)
{
    FILE *fp;
    int   i;

    fp = fopen("numbers.txt", "w");
    if (fp == NULL) {
        perror("fopen");
        return 1;
    }

    for (i = 1; i <= 5; i++)
        fprintf(fp, "Line %d: square = %d\n", i, i * i);

    fclose(fp);
    printf("Written to numbers.txt\n");
    return 0;
}
Written to numbers.txt

File contents of numbers.txt:

Line 1: square = 1
Line 2: square = 4
Line 3: square = 9
Line 4: square = 16
Line 5: square = 25

Reading Text from a File

fgets reads one line at a time and is safe — it never overflows the buffer:

#include <stdio.h>

int main(void)
{
    FILE *fp;
    char  buf[128];

    fp = fopen("numbers.txt", "r");
    if (fp == NULL) {
        perror("fopen");
        return 1;
    }

    while (fgets(buf, sizeof(buf), fp) != NULL)
        printf("%s", buf);

    fclose(fp);
    return 0;
}
Line 1: square = 1
Line 2: square = 4
Line 3: square = 9
Line 4: square = 16
Line 5: square = 25

fgets keeps the newline at the end of buf. That is why the printf above does not add \n. Use fscanf when you need to parse structured data field by field.

Reading and Writing Structs — Binary Files

fwrite and fread transfer raw bytes. This is the standard way to persist struct records:

#include <stdio.h>
#include <string.h>

struct Student {
    int  id;
    char name[32];
    float gpa;
};

int main(void)
{
    struct Student students[3] = {
        {1, "Alice", 3.8f},
        {2, "Bob",   3.2f},
        {3, "Carol", 3.9f}
    };
    struct Student s;
    FILE *fp;
    int   i;

    /* Write */
    fp = fopen("students.dat", "wb");
    if (fp == NULL) { perror("fopen"); return 1; }
    fwrite(students, sizeof(struct Student), 3, fp);
    fclose(fp);

    /* Read back */
    fp = fopen("students.dat", "rb");
    if (fp == NULL) { perror("fopen"); return 1; }

    printf("%-4s  %-20s  %s\n", "ID", "Name", "GPA");
    while (fread(&s, sizeof(struct Student), 1, fp) == 1)
        printf("%-4d  %-20s  %.2f\n", s.id, s.name, s.gpa);

    fclose(fp);
    return 0;
}
ID    Name                  GPA
1     Alice                 3.80
2     Bob                   3.20
3     Carol                 3.90

Random Access — fseek and ftell

fseek positions the file pointer anywhere in the file; ftell returns the current position. Together they enable reading or updating a specific record without reading the whole file:

#include <stdio.h>
#include <string.h>

struct Student { int id; char name[32]; float gpa; };

int main(void)
{
    struct Student s;
    FILE *fp;
    long  file_size;

    /* Read the second record (index 1) */
    fp = fopen("students.dat", "rb");
    if (fp == NULL) { perror("fopen"); return 1; }

    fseek(fp, 1 * (long)sizeof(struct Student), SEEK_SET);
    fread(&s, sizeof(struct Student), 1, fp);
    printf("Record 2: %s, GPA %.2f\n", s.name, s.gpa);

    /* Find file size */
    fseek(fp, 0, SEEK_END);
    file_size = ftell(fp);
    printf("File size: %ld bytes (%ld records)\n",
           file_size, file_size / (long)sizeof(struct Student));

    fclose(fp);
    return 0;
}
Record 2: Bob, GPA 3.20
File size: 144 bytes (3 records)

fseek takes three arguments: the file pointer, a byte offset, and a reference point — SEEK_SET (start), SEEK_CUR (current), or SEEK_END (end).

Appending to a File

#include <stdio.h>

int main(void)
{
    FILE *fp = fopen("log.txt", "a");
    if (fp == NULL) { perror("fopen"); return 1; }

    fprintf(fp, "Session started\n");
    fclose(fp);

    printf("Appended to log.txt\n");
    return 0;
}

Mode "a" always writes at the end, even if another process is writing to the file concurrently (on most systems). It never truncates an existing file.

Copying a File

#include <stdio.h>

int main(void)
{
    FILE *src, *dst;
    int   ch;

    src = fopen("numbers.txt", "r");
    if (src == NULL) { perror("source"); return 1; }

    dst = fopen("numbers_copy.txt", "w");
    if (dst == NULL) { perror("dest"); fclose(src); return 1; }

    while ((ch = fgetc(src)) != EOF)
        fputc(ch, dst);

    fclose(src);
    fclose(dst);
    printf("File copied.\n");
    return 0;
}
File copied.

For large files, copy in chunks using fread/fwrite with a buffer (e.g., 4096 bytes at a time) rather than character by character.

Error Handling

Function Returns on error How to check
fopen NULL if (fp == NULL) perror(...);
fread count < requested check return value; then feof() vs ferror()
fwrite count < requested check return value; use ferror()
fseek non-zero if (fseek(...) != 0) perror(...);
fclose EOF always check — a buffered write may fail here

perror prints the system error message for the last failed call. ferror(fp) returns non-zero if the file has an error flag set. feof(fp) returns non-zero if the end-of-file flag is set.

How to Compile

gcc -ansi -Wall -Wextra -o fileio fileio.c

Related C Programs

📖 Chapter 7 of The C Programming Language by K&R (Amazon.in) · Amazon.com covers standard I/O in full, including formatting, error handling, and the file access model that underlies everything in <stdio.h>.

Preparing for a C interview? Practice with the C Programming Quiz — 150+ questions, free on Android.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>