Skip to main content

Advanced Topics

File Handling & Command Line Arguments

Master C File Handling — what a file is, why files, file operations and modes, all file functions in table format, binary files, random access, and command line arguments — using LEARNCODEGURU’s 6-level learning model.

Level 1 Basics Content

C File Handling Overview

Everything a program stores in variables disappears when it ends. A file keeps data on disk so it survives after the program exits. This level covers what a file is, why we need files, the file operations, a complete table of file functions, and command line arguments.

Topic 1

What is a file?

A file is a named collection of data stored permanently on a secondary storage device (hard disk, SSD, pen drive). In C you never touch the disk directly — you work through a stream, represented by a FILE * pointer.


#include <stdio.h>

FILE *fp;                      /* a file pointer */

fp = fopen("data.txt", "w");  /* open (connect the stream) */
if (fp == NULL) return 1;   /* ALWAYS check */

fprintf(fp, "Hello file\n");  /* write */
fclose(fp);                  /* close (flush and disconnect) */

Two kinds of files in C:

# Aspect Text file Binary file
1 Stores Human-readable characters Raw bytes exactly as in memory
2 Mode string "r", "w", "a" "rb", "wb", "ab"
3 Number 12345 stored as 5 characters 4 bytes (an int)
4 Newline translation Yes on Windows (\n ↔ \r\n) None — bytes pass through
5 Viewable in Notepad Yes No — looks like garbage
6 Typical functions fprintf, fscanf, fgets fwrite, fread
7 Portable across machines Yes No — padding and endianness differ

Three streams are already open when your program starts:

  • stdin — standard input (keyboard)
  • stdout — standard output (screen)
  • stderr — standard error (screen, unbuffered)
Declare FILE * → fopen → check for NULL → read / write → fclose
Topic 2

Why files?

  • Persistence — data survives after the program ends and the machine restarts
  • Large volumes — store far more than fits comfortably in RAM
  • No re-entry — the user does not retype the same data on every run
  • Sharing — one program writes, another reads; files connect programs
  • Reporting — save results, logs, and invoices for later inspection
  • Backup and transfer — copy, email, or archive the data
  • Automated testing — feed a prepared input file instead of typing
Variables live in RAM (volatile, fast, small). Files live on disk (permanent, slower, large). File handling is how a program moves data between the two.
Topic 3

File operations

Every file program follows the same five steps.

# Operation Function(s) Purpose
1 Declare a file pointer FILE *fp; Holds the connection to the file
2 Open fopen Connect the stream in a given mode
3 Write / Read fprintf, fputs, fputc, fwrite / fscanf, fgets, fgetc, fread Transfer data both ways
4 Position (optional) fseek, ftell, rewind Move within the file for random access
5 Close fclose Flush the buffer and release the handle

Opening modes

# Mode Meaning If the file exists If it does not exist
1 "r" Read only Opens at the beginning Returns NULL
2 "w" Write only Erases all contents Creates it
3 "a" Append Writes at the end Creates it
4 "r+" Read and write Opens, contents preserved Returns NULL
5 "w+" Read and write Erases all contents Creates it
6 "a+" Read and append Reads anywhere, writes at the end Creates it
7 "rb", "wb", "ab"… Same modes, binary No newline translation Same as text counterpart
The most expensive beginner mistake: using "w" when you meant "a". The file is emptied the instant fopen succeeds, before you write a single byte.
Topic 4

File functions in table format

(a) Open and close

# Function Prototype Returns Use
1 fopen FILE *fopen(const char *name, const char *mode) File pointer or NULL Open a file
2 fclose int fclose(FILE *fp) 0 or EOF Flush and close
3 freopen FILE *freopen(const char *, const char *, FILE *) File pointer or NULL Redirect an existing stream
4 fflush int fflush(FILE *fp) 0 or EOF Force buffered output to disk

(b) Character and string I/O

# Function Prototype Returns Use
5 fgetc int fgetc(FILE *fp) Character or EOF Read one character
6 fputc int fputc(int c, FILE *fp) Character or EOF Write one character
7 fgets char *fgets(char *s, int n, FILE *fp) s or NULL Read one line safely
8 fputs int fputs(const char *s, FILE *fp) Non-negative or EOF Write a string (no newline added)
9 getc / putc Same as fgetc / fputc Character or EOF Macro versions, often faster

(c) Formatted I/O

# Function Prototype Returns Use
10 fprintf int fprintf(FILE *fp, const char *fmt, ...) Characters written Formatted write
11 fscanf int fscanf(FILE *fp, const char *fmt, ...) Items successfully read Formatted read

(d) Block (binary) I/O

# Function Prototype Returns Use
12 fwrite size_t fwrite(const void *p, size_t size, size_t n, FILE *fp) Items written Write structures / arrays
13 fread size_t fread(void *p, size_t size, size_t n, FILE *fp) Items read Read structures / arrays

(e) Positioning and status

# Function Prototype Returns Use
14 fseek int fseek(FILE *fp, long off, int origin) 0 on success Jump to a position
15 ftell long ftell(FILE *fp) Current offset or -1L Where am I in the file
16 rewind void rewind(FILE *fp) Nothing Go back to the start
17 feof int feof(FILE *fp) Non-zero at end of file Check after a failed read
18 ferror int ferror(FILE *fp) Non-zero if an error occurred Distinguish error from end of file
19 remove int remove(const char *name) 0 on success Delete a file
20 rename int rename(const char *old, const char *new) 0 on success Rename or move a file

fseek origins: SEEK_SET (start), SEEK_CUR (current position), SEEK_END (end).

Topic 5

Command line arguments

Instead of prompting the user, a program can receive values from the command line. Give main two parameters.


int main(int argc, char *argv[]) {
    /* argc = argument count  */
    /* argv = argument vector */
}

/* Running:  ./a.out data.txt 25          */
/* argc    = 3                            */
/* argv[0] = "./a.out"   (program name)   */
/* argv[1] = "data.txt"                   */
/* argv[2] = "25"        (a STRING)       */
/* argv[3] = NULL                         */
# Item Type Meaning
1 argc int Number of arguments, always ≥ 1
2 argv char *[] Array of strings
3 argv[0] char * Program name as invoked
4 argv[1] … char * The actual arguments
5 argv[argc] char * Always NULL

/* Print all arguments, with validation */
#include <stdio.h>

int main(int argc, char *argv[]) {
    int i;

    if (argc < 2) {
        printf("usage: %s arg1 arg2 ...\n", argv[0]);
        return 1;
    }

    for (i = 0; i < argc; i++)
        printf("argv[%d] = %s\n", i, argv[i]);

    return 0;
}

Arguments are strings — convert them yourself:


#include <stdlib.h>

int    n = atoi(argv[1]);   /* string  → int    */
double d = atof(argv[2]);   /* string  → double */
Why it matters here: command line arguments are how real tools take file names. ./copy source.txt dest.txt needs no prompts at all, which also makes the program usable inside scripts.
Example

Sample program — write then read a text file


/* The complete open-write-close-open-read-close cycle */
#include <stdio.h>

int main(void) {
    FILE *fp;
    char line[200];

    /* ---- write ---- */
    fp = fopen("notes.txt", "w");
    if (fp == NULL) {
        perror("notes.txt");
        return 1;
    }

    fprintf(fp, "C file handling\n");
    fprintf(fp, "Roll = %d, Marks = %.2f\n", 101, 88.5);
    fputs("last line\n", fp);

    fclose(fp);

    /* ---- read back ---- */
    fp = fopen("notes.txt", "r");
    if (fp == NULL) {
        perror("notes.txt");
        return 1;
    }

    printf("File contents:\n");
    while (fgets(line, sizeof line, fp) != NULL)
        printf("%s", line);   /* fgets keeps the \n */

    fclose(fp);

    return 0;
}
What to observe: every fopen is checked, the read loop tests fgets itself instead of feof, and both opens have a matching fclose.

Level 2 Advanced Content

Going deeper into file handling

Level 1 covered files, modes, operations, the function table, and command line arguments. Level 2 explains binary records, random access with fseek/ftell, error handling, buffering, the feof trap, and common mistakes.

Topic 1

Binary files — fwrite and fread with structures


struct Student {
    char  name[50];
    int   roll;
    float marks;
};

struct Student s = { "Asha", 101, 88.5 };

FILE *fp = fopen("rec.dat", "wb");   /* note the b */
fwrite(&s, sizeof s, 1, fp);           /* returns 1 on success */
fclose(fp);

struct Student t;
fp = fopen("rec.dat", "rb");
if (fread(&t, sizeof t, 1, fp) == 1)
    printf("%s %d %.2f\n", t.name, t.roll, t.marks);
fclose(fp);
  • Both take size × count and return the count transferred
  • A short return means end of file or an error — test it, do not ignore it
  • Binary records are fixed size, which is what makes random access possible
  • Not portable: padding and endianness differ between machines
Topic 2

Random access — fseek, ftell, rewind


fseek(fp, 0L, SEEK_SET);    /* first byte              */
fseek(fp, 0L, SEEK_END);    /* just past the last byte */
fseek(fp, -10L, SEEK_CUR);  /* 10 bytes back           */

/* Size of a file in bytes */
fseek(fp, 0L, SEEK_END);
long size = ftell(fp);
rewind(fp);                  /* back to the start */

/* Jump straight to record number k (0-based) */
fseek(fp, k * (long)sizeof(struct Student), SEEK_SET);
fread(&t, sizeof t, 1, fp);
Origin Value Offset measured from
SEEK_SET 0 Beginning of the file
SEEK_CUR 1 Current position
SEEK_END 2 End of the file
Random access is why fixed-size binary records are worth the loss of readability: reaching record 5000 costs one fseek instead of reading 4999 records first.
Topic 3

The feof trap


/* WRONG — prints the last value twice */
while (!feof(fp)) {
    fscanf(fp, "%d", &n);
    printf("%d\n", n);
}

/* RIGHT — the read itself is the condition */
while (fscanf(fp, "%d", &n) == 1)
    printf("%d\n", n);

/* RIGHT — line based */
while (fgets(line, sizeof line, fp) != NULL)
    fputs(line, stdout);

/* RIGHT — character based (note: int, not char) */
int ch;
while ((ch = fgetc(fp)) != EOF)
    putchar(ch);
feof reports what already happened. It only turns true after a read has failed, so a loop that tests it first always runs one extra iteration on stale data.
Topic 4

Error handling and buffering


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

FILE *fp = fopen("missing.txt", "r");
if (fp == NULL) {
    perror("missing.txt");                       /* prints the OS reason  */
    fprintf(stderr, "%s\n", strerror(errno));  /* same text, as string  */
    return 1;
}

/* After a loop, tell end-of-file apart from a real error */
if (ferror(fp))
    fprintf(stderr, "read error\n");
  • Buffering — output sits in memory until the buffer fills, fflush, or fclose
  • stderr is unbuffered so error messages appear immediately
  • fflush(stdin) is undefined behaviour — flushing is for output
  • An abnormal exit without fclose can leave a file empty on disk
Topic 5

A real tool — copy using command line arguments


/* usage: ./copy source.txt dest.txt */
#include <stdio.h>

int main(int argc, char *argv[]) {
    FILE *in, *out;
    int ch;

    if (argc != 3) {
        fprintf(stderr, "usage: %s source dest\n", argv[0]);
        return 1;
    }

    in = fopen(argv[1], "rb");
    if (in == NULL) {
        perror(argv[1]);
        return 1;
    }

    out = fopen(argv[2], "wb");
    if (out == NULL) {
        perror(argv[2]);
        fclose(in);           /* close on the error path too */
        return 1;
    }

    while ((ch = fgetc(in)) != EOF)
        fputc(ch, out);

    fclose(in);
    fclose(out);

    printf("copied %s to %s\n", argv[1], argv[2]);
    return 0;
}
Topic 6

Common mistakes

Mistake Why it hurts Fix
Not checking fopen Crash on a NULL stream if (fp == NULL) { perror(...); return 1; }
Using "w" to add data Existing contents erased Use "a" to append
while (!feof(fp)) Last record processed twice Loop on the read call's return value
char ch = fgetc(fp); EOF may be indistinguishable from data Declare int ch;
Forgetting fclose Buffered data never written Close on every exit path
fflush(stdin) Undefined behaviour Read and discard until \n
Not stripping \n after fgets String comparisons fail invisibly s[strcspn(s, "\n")] = '\0';
Mixing text and binary modes Newline translation corrupts binary data Use "rb" / "wb" for binary
Reading argv[1] without checking argc Reads past the array Validate argc first
Treating argv[1] as a number It is a string atoi / strtol

Level 3 C Tricky Interview Points

Advanced tricky interview points on FILE pointers, modes, EOF handling, buffering, binary records, and command line arguments.

Tricky Interview Point-1:

FILE is an opaque structure — you always use FILE *, never FILE

FILE holds the buffer, position indicator, and error flags. The standard never says what is inside it, so you only ever handle the pointer that fopen returns.

Tricky Interview Point-2:

fopen returns NULL on failure — checking it is not optional

A missing file, a bad path, or no permission all return NULL. Using that NULL pointer with fprintf or fscanf is undefined behaviour and usually a crash.

Tricky Interview Point-3:

"w" truncates an existing file to zero length immediately

Opening with "w" destroys the previous contents the moment fopen succeeds, even if you never write anything. Use "a" to append or "r+" to update in place.

Tricky Interview Point-4:

"r+" and "w+" both allow reading and writing, but differ at open time

"r+" requires the file to exist and preserves it; "w+" creates or truncates. "a+" always writes at the end regardless of where you positioned the file.

Tricky Interview Point-5:

In text mode on Windows, \n is translated to \r\n on the way out

That is why the byte count on disk can exceed what you wrote, and why ftell offsets in text mode are only meaningful when fed back to fseek.

Tricky Interview Point-6:

getc/fgetc return int, not char — because of EOF

EOF is typically -1 and must be distinguishable from every valid byte including 0xFF. Storing the result in a char breaks the comparison on some platforms.

Tricky Interview Point-7:

while (!feof(fp)) is the classic wrong loop

feof only becomes true after a read has already failed, so the last item gets processed twice. Loop on the read call itself: while (fgets(...) != NULL) or while (fscanf(...) == 1).

Tricky Interview Point-8:

fgets keeps the newline; gets never existed safely and was removed

fgets stores the \n if there was room, so you usually strip it. gets had no bound and was removed in C11 — never use it.

Tricky Interview Point-9:

fread and fwrite work in units of size × count and return the count

fwrite(&s, sizeof s, 1, fp) returns 1 on success, not the byte count. A short return means a partial or failed transfer, which is how you detect a full disk.

Tricky Interview Point-10:

Binary records written by fwrite are not portable

Padding, endianness, and type sizes differ across compilers and machines. Binary files are fine for one program on one platform; use text for interchange.

Tricky Interview Point-11:

fflush is defined for output streams only

fflush(stdin) is undefined behaviour even though it appears in many textbooks. To discard leftover input, read and discard characters until \n or EOF.

Tricky Interview Point-12:

fclose flushes buffers — skipping it can silently lose data

Buffered output only reaches the disk on flush or close. A program that exits abnormally without closing may leave the file empty or truncated.

Tricky Interview Point-13:

argc is always at least 1, and argv[0] is the program name

Real arguments start at argv[1], and argv[argc] is guaranteed to be NULL. That trailing NULL lets you walk argv without using argc at all.

Tricky Interview Point-14:

Command line arguments are always strings

./a.out 25 passes the two characters "25", not the integer 25. Convert with atoi or, better, strtol so you can detect malformed input.

Tricky Interview Point-15:

The shell expands wildcards before your program starts

./a.out *.txt gives your program one argument per matching file, not the literal "*.txt". Quote the argument if you want the pattern itself.

Interview tip: if you mention checking fopen, never looping on feof, and always closing the file, you have already covered what most interviewers are listening for.

Level 4 Simple Programs

Short programs covering writing, reading, counting, copying, binary records, and command line arguments.

Program 1 — Create a file and write lines into it


/* Write three lines to a text file */
#include <stdio.h>

int main(void) {
    FILE *fp = fopen("data.txt", "w");

    if (fp == NULL) {
        perror("data.txt");
        return 1;
    }

    fputs("Line one\n", fp);
    fputs("Line two\n", fp);
    fprintf(fp, "Line %d\n", 3);

    fclose(fp);
    printf("data.txt written\n");

    return 0;
}

Program 2 — Display a file on screen


/* Character by character until EOF */
#include <stdio.h>

int main(void) {
    FILE *fp = fopen("data.txt", "r");
    int ch;                 /* int, not char */

    if (fp == NULL) {
        perror("data.txt");
        return 1;
    }

    while ((ch = fgetc(fp)) != EOF)
        putchar(ch);

    fclose(fp);
    return 0;
}

Program 3 — Count characters, words, and lines


/* A miniature wc */
#include <stdio.h>
#include <ctype.h>

int main(void) {
    FILE *fp = fopen("data.txt", "r");
    int ch, inWord = 0;
    long chars = 0, words = 0, lines = 0;

    if (fp == NULL) {
        perror("data.txt");
        return 1;
    }

    while ((ch = fgetc(fp)) != EOF) {
        chars++;
        if (ch == '\n')
            lines++;

        if (isspace(ch))
            inWord = 0;
        else if (!inWord) {
            inWord = 1;
            words++;
        }
    }

    fclose(fp);

    printf("chars = %ld\nwords = %ld\nlines = %ld\n", chars, words, lines);
    return 0;
}

Program 4 — Append a line to an existing file


/* "a" keeps the old contents */
#include <stdio.h>
#include <string.h>

int main(void) {
    FILE *fp;
    char line[200];

    printf("Text to append: ");
    if (fgets(line, sizeof line, stdin) == NULL)
        return 1;

    line[strcspn(line, "\n")] = '\0';   /* strip the newline */

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

    fprintf(fp, "%s\n", line);
    fclose(fp);

    printf("appended\n");
    return 0;
}

Program 5 — Binary file: write and read a structure


/* fwrite / fread with a record */
#include <stdio.h>

struct Student {
    char  name[50];
    int   roll;
    float marks;
};

int main(void) {
    struct Student s = { "Asha", 101, 88.5 };
    struct Student t;
    FILE *fp;

    fp = fopen("rec.dat", "wb");
    if (fp == NULL) { perror("rec.dat"); return 1; }
    fwrite(&s, sizeof s, 1, fp);
    fclose(fp);

    fp = fopen("rec.dat", "rb");
    if (fp == NULL) { perror("rec.dat"); return 1; }

    if (fread(&t, sizeof t, 1, fp) == 1)
        printf("%s %d %.2f\n", t.name, t.roll, t.marks);
    else
        printf("read failed\n");

    fclose(fp);
    return 0;
}

Program 6 — Sum of numbers given as command line arguments


/* usage: ./sum 10 20 30 */
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    int i, sum = 0;

    if (argc < 2) {
        fprintf(stderr, "usage: %s n1 n2 ...\n", argv[0]);
        return 1;
    }

    for (i = 1; i < argc; i++)
        sum += atoi(argv[i]);   /* string → int */

    printf("sum of %d numbers = %d\n", argc - 1, sum);
    return 0;
}
Comment Directive Type Keyword Function Variable String Number

Level 5 Problem Solving Tricks

How to master file handling while solving problems

Nearly every file bug comes from four habits being missing: checking fopen, looping on the read instead of feof, bounding fgets with sizeof, and closing on every exit path.

Trick 1

Check fopen every single time

if (fp == NULL) { perror("open"); return 1; } is three lines that turn a mysterious crash into a clear message naming the actual OS error.

Trick 2

Loop on the read function, never on feof

while (fgets(line, sizeof line, fp)) and while (fscanf(fp, "%d", &n) == 1) both stop exactly at the end. Using feof as the condition duplicates the last record.

Trick 3

Pass sizeof the buffer to fgets, not a hard-coded number

fgets(line, sizeof line, fp) stays correct when the array size changes. A literal 100 becomes a buffer overflow the day someone shrinks the array.

Trick 4

Strip the trailing newline that fgets leaves behind

line[strcspn(line, "\n")] = '\0'; is the one-line idiom. Without it, comparisons and concatenations quietly fail on an invisible character.

Trick 5

Close every file on every exit path

If a function returns early on an error, close the file first. A single fclose at the bottom is only reached when nothing goes wrong.

Trick 6

Use "a" when you mean append

Reaching for "w" to add a line silently wipes the file. "a" positions every write at the end and creates the file if it is missing.

Trick 7

Validate argc before touching argv

if (argc < 2) { printf("usage: %s <file>\n", argv[0]); return 1; } prevents reading past the array and gives the user a usable message.

Trick 8

Prefer strtol over atoi for numeric arguments

atoi returns 0 for "abc" with no way to tell it from a real 0. strtol reports where parsing stopped so you can reject bad input.

Quick success checklist

  • Before run: right mode? fopen checked? every open has an fclose?
  • Debug order: does the file exist → print the path → print each item as it is read → check ferror
  • Safety habit: fgets with sizeof → strip \n → validate argc before argv
Level-5 goal: treat a file like any other resource — acquire it, check it, use it, release it — and let the read function, not feof, tell you when to stop.

Level 6 Practice Programs List

Programming questions only — focused on file handling: creating, reading, counting, copying, appending, binary records, random access, and command line arguments.

  1. 1. Write a C program that creates a text file, writes three lines into it, and closes it.
  2. 2. Write a C program that opens an existing text file and displays its contents on screen character by character.
  3. 3. Write a C program that reads a file line by line using fgets and prints each line with its line number.
  4. 4. Write a C program that counts the number of characters, words, and lines in a text file.
  5. 5. Write a C program that copies the contents of one file into another file.
  6. 6. Write a C program that appends a new line to an existing file without erasing its contents.
  7. 7. Write a C program that writes a structure (student record) to a binary file using fwrite and reads it back with fread.
  8. 8. Write a C program that reads n integers from a file and prints their sum and average.
  9. 9. Write a C program that uses fseek and ftell to find the size of a file in bytes.
  10. 10. Write a C program that searches a file for a given word and prints how many times it occurs.
  11. 11. Write a C program that takes two file names as command line arguments and copies the first into the second.
  12. 12. Write a C program that accepts numbers as command line arguments and prints their sum.