โ† All units

๐Ÿ“ Unit 6: File Processing

How a C program saves things in a notebook (a file) so nothing is forgotten

1๏ธโƒฃ What is a file, and why do we need one?

You do your homework on a whiteboard. When Mom wipes it, everything is gone! So next time you write in a notebook. The notebook keeps your work even tomorrow.
๐Ÿง  RAM (memory) = whiteboard
Fast, but forgets everything when the program ends or power goes off. Variables live here.
๐Ÿ’พ File on disk = notebook
Slower, but remembers forever. Data is still there next time.

A file is a named place on the disk (hard disk / SSD) where data is stored permanently.

The 3 golden steps: OPEN โ†’ USE โ†’ CLOSE

StepC codeKid meaning
0. Make a bookmarkFILE *fp;A file pointer: a bookmark that remembers which notebook and which page
1. Openfp = fopen("hello.txt", "w");Open the notebook named hello.txt for writing
2. Checkif (fp == NULL)Could not open it? (missing file, no permission) Then stop!
3. Usefprintf, fgets, fputc ...Write or read
4. Closefclose(fp);Close the notebook so everything is really saved
Always check fp == NULL. If you use a NULL file pointer, your program crashes.
FILE (all capitals) is a structure defined in <stdio.h>. It stores details like where the cursor is and the buffer. We only use a pointer to it.
Need for files: data in variables is temporary (lost when the program ends); files store data permanently, can hold large data, and can be shared between programs. Steps: declare FILE *fp, open with fopen, check NULL, read/write, close with fclose.

File: 01_why_files_first_file.c

/*
 * Program 1: Why Files? Your very first file
 * ------------------------------------------
 * Real-life story: When you switch off a computer, everything in its
 * memory (RAM) is forgotten, like writing on a whiteboard and wiping it.
 * A FILE is like a notebook: what you write stays there even tomorrow.
 *
 * Steps to use a file (remember: OPEN -> USE -> CLOSE):
 *   1. FILE *fp;              -> a "file pointer", like a bookmark for the notebook
 *   2. fp = fopen(name, mode) -> open the notebook
 *   3. fprintf / fgets ...    -> write or read
 *   4. fclose(fp)             -> close the notebook (very important!)
 */
#include <stdio.h>

int main() {
    FILE *fp;                              /* our bookmark for the file */

    /* ---- WRITE into the file ---- */
    fp = fopen("hello.txt", "w");          /* "w" = open for writing (makes a new file) */
    if (fp == NULL) {                      /* NULL means "could not open" */
        printf("Sorry, could not open the file!\n");
        return 1;
    }
    fprintf(fp, "Hello, I am saved in a file!\n");
    fprintf(fp, "I will still be here tomorrow.\n");
    fclose(fp);                            /* close it so the words are really saved */
    printf("Wrote 2 lines into hello.txt\n\n");

    /* ---- READ it back ---- */
    char line[100];                        /* a box to hold one line */
    fp = fopen("hello.txt", "r");          /* "r" = open for reading */
    if (fp == NULL) {
        printf("Sorry, could not open the file!\n");
        return 1;
    }
    printf("Reading hello.txt:\n");
    while (fgets(line, sizeof(line), fp) != NULL) {   /* read one line at a time */
        printf("  %s", line);
    }
    fclose(fp);

    return 0;
}

Output

Wrote 2 lines into hello.txt

Reading hello.txt:
  Hello, I am saved in a file!
  I will still be here tomorrow.

2๏ธโƒฃ Input & Output: the concept of a file (streams)

Think of a water pipe. Water flows through it drop by drop. In C, data flows through a pipe called a stream, one byte after another.

C treats everything as a stream of bytes: a file on disk, the keyboard, and the screen. So reading from the keyboard and reading from a file look almost the same!

โŒจ๏ธ Keyboardstdin โ†’ your program
your program โ†’ stdout๐Ÿ–ฅ๏ธ Screen
your program โ†’ stderr๐Ÿšจ Screen (errors)
StreamNameConnected toUsed by
stdinStandard inputKeyboardscanf, getchar, fgets(.., stdin)
stdoutStandard outputScreenprintf, putchar
stderrStandard errorScreenfprintf(stderr, ...) for error messages
These 3 streams are opened automatically when your program starts. You never fopen them.
Why a separate error pipe? If you send normal output to a file (./prog > out.txt), error messages still show on the screen, so you don't miss them. That's also why, in the output below, the stderr message is shown separately.
Stream = a logical sequence of bytes flowing between the program and a device/file. Two kinds: text stream and binary stream. Standard streams: stdin, stdout, stderr. Buffer = a temporary memory area that collects data before it is actually written to disk.

File: 02_streams_stdin_stdout_stderr.c

/*
 * Program 2: Concept of a file - Streams (stdin, stdout, stderr)
 * --------------------------------------------------------------
 * Real-life story: Think of a STREAM as a water pipe.
 * Data flows through it, one character after another, like water.
 *
 * Every C program gets 3 pipes for free, already open:
 *   stdin  -> the INPUT pipe  (keyboard)        - like your ears
 *   stdout -> the OUTPUT pipe (screen)          - like your mouth
 *   stderr -> the ERROR pipe  (screen, for problems) - like shouting "Help!"
 *
 * In C, the keyboard and the screen are treated just like files!
 * That's why fprintf(stdout, ...) works the same as printf(...).
 *
 * Sample input: Sabari
 */
#include <stdio.h>

int main() {
    char name[50];

    fprintf(stdout, "What is your name? ");     /* same as printf */
    if (fgets(name, sizeof(name), stdin) == NULL) {  /* read from the keyboard pipe */
        fprintf(stderr, "Error: no name was typed!\n");  /* errors go to stderr */
        return 1;
    }

    fprintf(stdout, "Hello, %s", name);          /* name already has the Enter (\n) */
    fprintf(stderr, "(This message travelled through the stderr pipe)\n");

    return 0;
}

Output (typed input: Sabari)

What is your name? Hello, Sabari

--- what came through stderr ---
(This message travelled through the stderr pipe)

3๏ธโƒฃ fopen modes: how do you want to open the notebook?

A shopping list on the fridge. Do you want to read it, tear it up and write a new one, or add an item at the bottom?
ModeMeaningIf file existsIf file is missing
"r"Read onlyOpens, cursor at startReturns NULL
"w"Write onlyWipes it clean (old data lost!)Creates a new file
"a"Append (add at end)Keeps old data, writes at the endCreates a new file
"r+"Read + writeOpens, keeps data, cursor at startReturns NULL
"w+"Write + readWipes it cleanCreates a new file
"a+"Read + appendKeeps data, writes always go to endCreates a new file
"rb" "wb" "ab"
"rb+" "wb+" "ab+"
Same as above, but for binary filesAdd b when you use fwrite/fread with raw data
๐Ÿงช Mode chooser: what happens to my file?
The file shopping.txt currently contains:
milk bread
Mode: File exists? Write this:
"w" is dangerous: it erases the file the moment you open it, even if you write nothing.
Syntax: FILE *fopen(const char *filename, const char *mode); Returns a FILE pointer on success, NULL on failure. List all modes r, w, a, r+, w+, a+ (and their binary versions with b) with their behaviour for existing and missing files.

File: 03_fopen_modes.c

/*
 * Program 3: fopen modes - "w", "a", "r", "r+"
 * --------------------------------------------
 * Real-life story: A shopping list on the fridge.
 *   "w" -> tear off the old page and write a NEW list (old list is gone!)
 *   "a" -> add items at the BOTTOM of the list (append)
 *   "r" -> only READ the list, no writing
 *   "r+"-> read AND change the list (file must already exist)
 */
#include <stdio.h>

/* a small helper that prints the whole file */
void showFile(const char *name) {
    FILE *fp = fopen(name, "r");
    int ch;
    if (fp == NULL) {
        printf("(cannot open %s)\n", name);
        return;
    }
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch);
    }
    fclose(fp);
}

int main() {
    FILE *fp;

    /* "w" : write a fresh list */
    fp = fopen("shopping.txt", "w");
    if (fp == NULL) return 1;
    fprintf(fp, "milk\nbread\n");
    fclose(fp);
    printf("After \"w\" (new list):\n");
    showFile("shopping.txt");

    /* "a" : add to the end, old items stay */
    fp = fopen("shopping.txt", "a");
    if (fp == NULL) return 1;
    fprintf(fp, "eggs\n");
    fclose(fp);
    printf("\nAfter \"a\" (added eggs):\n");
    showFile("shopping.txt");

    /* "r+" : change the first letter of the file */
    fp = fopen("shopping.txt", "r+");
    if (fp == NULL) return 1;
    fputc('M', fp);                /* overwrite 'm' with 'M' */
    fclose(fp);
    printf("\nAfter \"r+\" (changed m to M):\n");
    showFile("shopping.txt");

    /* "w" again : WARNING, it wipes everything! */
    fp = fopen("shopping.txt", "w");
    if (fp == NULL) return 1;
    fprintf(fp, "chocolate\n");
    fclose(fp);
    printf("\nAfter \"w\" again (old list wiped!):\n");
    showFile("shopping.txt");

    /* "r" on a file that does not exist gives NULL */
    fp = fopen("no_such_file.txt", "r");
    if (fp == NULL) {
        printf("\n\"r\" on a missing file -> fopen gives NULL\n");
    }

    return 0;
}

Output

After "w" (new list):
milk
bread

After "a" (added eggs):
milk
bread
eggs

After "r+" (changed m to M):
Milk
bread
eggs

After "w" again (old list wiped!):
chocolate

"r" on a missing file -> fopen gives NULL

4๏ธโƒฃ File I/O operations: one character at a time (fputc, fgetc, EOF, feof)

Threading beads on a string one bead at a time, then pulling them off one by one until the string is empty.
FunctionWhat it doesReturns
fputc(ch, fp)Writes one characterThe character, or EOF on error
fgetc(fp)Reads one character, cursor moves 1 stepThe character as an int, or EOF at the end
feof(fp)Asks "did we already reach the end?"Non-zero (true) after the end was hit
fclose(fp)Saves and closes the file0 on success
getc / putcSame as fgetc / fputc
EOF means "End Of File". It's a special value (usually -1), not a real letter. That's why we store fgetc's answer in an int ch, not a char.
while (!feof(fp)) is a common mistake: feof only becomes true after a read fails, so the last item gets processed twice. Check the return value of the read function instead, like while ((ch = fgetc(fp)) != EOF).
Character I/O functions: fgetc/getc, fputc/putc. Line I/O: fgets/fputs. Formatted I/O: fscanf/fprintf. Block I/O: fread/fwrite. Write one example for each group.

File: 04_fputc_fgetc_feof.c

/*
 * Program 4: Character I/O - fputc, fgetc, EOF and feof
 * ----------------------------------------------------
 * Real-life story: Threading beads on a string ONE bead at a time,
 * then taking them off one by one until the string is empty.
 *
 *   fputc(ch, fp)  -> put ONE character into the file
 *   fgetc(fp)      -> get ONE character from the file
 *   EOF            -> "End Of File", a special signal: no more beads!
 *   feof(fp)       -> asks "did we already reach the end?" (1 = yes)
 */
#include <stdio.h>

int main() {
    FILE *fp;
    char word[] = "BEADS";
    int i, ch;
    int letters = 0;

    /* write the word one character at a time */
    fp = fopen("beads.txt", "w");
    if (fp == NULL) {
        printf("Could not open file\n");
        return 1;
    }
    for (i = 0; word[i] != '\0'; i++) {
        fputc(word[i], fp);              /* thread one bead */
    }
    fclose(fp);
    printf("Saved \"%s\" into beads.txt\n", word);

    /* read it back one character at a time */
    fp = fopen("beads.txt", "r");
    if (fp == NULL) {
        printf("Could not open file\n");
        return 1;
    }
    printf("Taking beads off: ");
    while ((ch = fgetc(fp)) != EOF) {    /* keep going until End Of File */
        printf("[%c] ", ch);
        letters++;
    }
    printf("\nTotal characters = %d\n", letters);

    if (feof(fp)) {                      /* confirm we hit the end */
        printf("feof says: yes, we reached the end of the file.\n");
    }
    fclose(fp);

    return 0;
}

Output

Saved "BEADS" into beads.txt
Taking beads off: [B] [E] [A] [D] [S] 
Total characters = 5
feof says: yes, we reached the end of the file.

5๏ธโƒฃ One line at a time (fputs, fgets)

Writing a poem in your notebook line by line, then reading it aloud line by line.
FunctionWhat it does
fputs(str, fp)Writes the whole string. It does not add \n by itself; you must put it.
fgets(buf, n, fp)Reads up to n-1 characters, or until \n. Keeps the \n, adds '\0'. Returns NULL when there are no more lines.
fgets is safe because you tell it the size of your box. It will never overflow your array.
fgets vs gets: gets (removed from C) has no size limit and can overflow the buffer; fgets takes a size and also works on any file.

File: 05_fputs_fgets_lines.c

/*
 * Program 5: Line I/O - fputs and fgets
 * -------------------------------------
 * Real-life story: Writing a poem in a notebook LINE by line,
 * and then reading it aloud line by line, with line numbers.
 *
 *   fputs(text, fp)          -> write a whole string (line)
 *   fgets(box, size, fp)     -> read ONE line (at most size-1 letters)
 *                               gives NULL when there are no more lines
 */
#include <stdio.h>

int main() {
    FILE *fp;
    char line[100];
    int lineNo = 0;

    fp = fopen("poem.txt", "w");
    if (fp == NULL) {
        printf("Could not open file\n");
        return 1;
    }
    fputs("Twinkle twinkle little star\n", fp);
    fputs("How I wonder what you are\n", fp);
    fputs("Up above the world so high\n", fp);
    fputs("Like a diamond in the sky\n", fp);
    fclose(fp);

    fp = fopen("poem.txt", "r");
    if (fp == NULL) {
        printf("Could not open file\n");
        return 1;
    }
    while (fgets(line, sizeof(line), fp) != NULL) {
        lineNo++;
        printf("Line %d: %s", lineNo, line);
    }
    fclose(fp);

    printf("\nThe poem has %d lines.\n", lineNo);
    return 0;
}

Output

Line 1: Twinkle twinkle little star
Line 2: How I wonder what you are
Line 3: Up above the world so high
Line 4: Like a diamond in the sky

The poem has 4 lines.

6๏ธโƒฃ Formatted I/O (fprintf, fscanf)

The teacher's mark register: every line has a roll number, a name and marks, in neat columns.

You already know printf and scanf. Just put an f in front and give the file pointer first:

Screen / keyboardFile
printf("%d %s", roll, name);fprintf(fp, "%d %s", roll, name);
scanf("%d %s", &roll, name);fscanf(fp, "%d %s", &roll, name);
fscanf returns how many items it read. Reading 3 items per line? Loop while it returns 3. At the end of the file it returns EOF.
โœ๏ธ Try it: build a line with fprintf
Roll Name Marks
1 Anu 85 2 Bala 72 3 Chitra 94 4 Dinesh 60

  
fprintf/fscanf are used for formatted data in text files. Syntax: int fprintf(FILE *fp, const char *format, ...); and int fscanf(FILE *fp, const char *format, ...);

File: 06_fprintf_fscanf_marks.c

/*
 * Program 6: Formatted I/O - fprintf and fscanf
 * ---------------------------------------------
 * Real-life story: The teacher's mark register.
 * Each line has: roll number, name, marks.
 *
 *   fprintf(fp, "%d %s %d\n", ...) -> like printf, but writes into a FILE
 *   fscanf (fp, "%d %s %d", ...)   -> like scanf, but reads from a FILE
 *                                     returns how many items it read
 */
#include <stdio.h>

int main() {
    FILE *fp;
    int roll, marks;
    char name[30];
    int total = 0, count = 0;

    /* the teacher writes the register */
    fp = fopen("marks.txt", "w");
    if (fp == NULL) {
        printf("Could not open file\n");
        return 1;
    }
    fprintf(fp, "%d %s %d\n", 1, "Anu", 85);
    fprintf(fp, "%d %s %d\n", 2, "Bala", 72);
    fprintf(fp, "%d %s %d\n", 3, "Chitra", 94);
    fprintf(fp, "%d %s %d\n", 4, "Dinesh", 60);
    fclose(fp);

    /* the principal reads the register */
    fp = fopen("marks.txt", "r");
    if (fp == NULL) {
        printf("Could not open file\n");
        return 1;
    }
    printf("Roll  Name      Marks\n");
    printf("---------------------\n");
    while (fscanf(fp, "%d %29s %d", &roll, name, &marks) == 3) {  /* 3 items read = OK */
        printf("%-5d %-9s %d\n", roll, name, marks);
        total = total + marks;
        count++;
    }
    fclose(fp);

    printf("---------------------\n");
    printf("Class average = %.2f\n", (float)total / count);
    return 0;
}

Output

Roll  Name      Marks
---------------------
1     Anu       85
2     Bala      72
3     Chitra    94
4     Dinesh    60
---------------------
Class average = 77.75

7๏ธโƒฃ Text files vs Binary files

Saving the number 123456789. A text file writes it like you would on paper: 9 digits. A binary file takes a photo of the computer's memory: always 4 bytes for an int.
Text fileBinary file
Stores data asReadable characters (ASCII)Raw bytes, exactly like in memory
Open in Notepad?Yes, you can read itShows strange symbols
Size of number 1234567899 bytes4 bytes (sizeof int)
NewlineMay be converted (e.g. \n โ†” \r\n on Windows)No conversion
End of fileDetected as EOFDetected by file size / fread return
SpeedSlower (number โ†” text conversion)Faster (direct copy)
Modes"r" "w" "a""rb" "wb" "ab"
Functionsfprintf, fscanf, fgets, fputsfwrite, fread
Example.txt, .c, .html.dat, .bin, .jpg, .exe
โš–๏ธ Try it: text size vs binary size
Number:

  
Differences between text and binary files (make a table like above: storage form, readability, size, newline conversion, EOF, speed, modes, functions).

File: 07_text_vs_binary.c

/*
 * Program 7: Text file vs Binary file
 * -----------------------------------
 * Real-life story: Saving the number 123456789.
 *   TEXT file   -> writes it like a human: '1','2','3',... (9 letters = 9 bytes)
 *                  you can open it in Notepad and read it.
 *   BINARY file -> copies the raw memory of the int (always 4 bytes)
 *                  Notepad shows funny symbols, but the computer reads it fast.
 */
#include <stdio.h>

int main() {
    int number = 123456789;
    FILE *fp;
    long textSize, binSize;

    /* TEXT */
    fp = fopen("number.txt", "w");          /* "w" = text write */
    if (fp == NULL) return 1;
    fprintf(fp, "%d", number);
    textSize = ftell(fp);                   /* how many bytes written so far */
    fclose(fp);

    /* BINARY */
    fp = fopen("number.bin", "wb");         /* "wb" = binary write */
    if (fp == NULL) return 1;
    fwrite(&number, sizeof(number), 1, fp); /* copy 1 int straight from memory */
    binSize = ftell(fp);
    fclose(fp);

    printf("Number saved      : %d\n", number);
    printf("Text file size    : %ld bytes (one byte per digit)\n", textSize);
    printf("Binary file size  : %ld bytes (size of an int)\n", binSize);

    /* read the binary number back */
    int readBack = 0;
    fp = fopen("number.bin", "rb");         /* "rb" = binary read */
    if (fp == NULL) return 1;
    fread(&readBack, sizeof(readBack), 1, fp);
    fclose(fp);
    printf("Read back from bin: %d\n", readBack);

    /* peek at the raw bytes inside the binary file */
    unsigned char bytes[4];
    fp = fopen("number.bin", "rb");
    if (fp == NULL) return 1;
    fread(bytes, 1, 4, fp);
    fclose(fp);
    printf("Raw bytes in .bin : %02X %02X %02X %02X (not readable by humans!)\n",
           bytes[0], bytes[1], bytes[2], bytes[3]);

    return 0;
}

Output

Number saved      : 123456789
Text file size    : 9 bytes (one byte per digit)
Binary file size  : 4 bytes (size of an int)
Read back from bin: 123456789
Raw bytes in .bin : 15 CD 5B 07 (not readable by humans!)
Why do the bytes look backwards (15 CD 5B 07)? 123456789 is 0x075BCD15. Most computers store the smallest byte first. This is called little-endian.

8๏ธโƒฃ Saving whole records: fwrite and fread

School ID cards. Instead of copying the roll, then the name, then the marks separately, we photocopy the whole card into the file in one go.
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *fp);
size_t fread (void *ptr,       size_t size, size_t count, FILE *fp);
              โ”‚                 โ”‚            โ”‚
              โ”‚                 โ”‚            โ””โ”€ how many items
              โ”‚                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ size of ONE item (use sizeof)
              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ address of the data (use &)
Both return how many items were really written or read. while (fread(&s, sizeof s, 1, fp) == 1) reads until no full record is left.
fread/fwrite are block (record) I/O functions used with binary files to store and read structures. Explain the 4 arguments.

File: 08_binary_struct_fwrite_fread.c

/*
 * Program 8: Binary file with a structure - fwrite and fread
 * ----------------------------------------------------------
 * Real-life story: School ID cards. Each card has roll, name and marks.
 * Instead of writing each piece separately, we photocopy the WHOLE card
 * into the file in one go with fwrite, and read the whole card with fread.
 *
 *   fwrite(&data, size_of_one, how_many, fp)
 *   fread (&data, size_of_one, how_many, fp)  -> returns how many it read
 */
#include <stdio.h>

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

int main() {
    struct Student cls[3] = {
        {1, "Anu",    85.5},
        {2, "Bala",   72.0},
        {3, "Chitra", 94.5}
    };
    struct Student s;
    FILE *fp;

    /* write all 3 cards at once */
    fp = fopen("students.dat", "wb");
    if (fp == NULL) {
        printf("Could not open file\n");
        return 1;
    }
    fwrite(cls, sizeof(struct Student), 3, fp);
    fclose(fp);
    printf("Saved 3 students. Each record = %zu bytes\n\n", sizeof(struct Student));

    /* read them back one card at a time */
    fp = fopen("students.dat", "rb");
    if (fp == NULL) {
        printf("Could not open file\n");
        return 1;
    }
    printf("Roll  Name     Marks\n");
    while (fread(&s, sizeof(struct Student), 1, fp) == 1) {   /* 1 card read = OK */
        printf("%-5d %-8s %.1f\n", s.roll, s.name, s.marks);
    }
    fclose(fp);

    return 0;
}

Output

Saved 3 students. Each record = 28 bytes

Roll  Name     Marks
1     Anu      85.5
2     Bala     72.0
3     Chitra   94.5

9๏ธโƒฃ Types of file processing: Sequential vs Random access

๐Ÿ“ผ Sequential access = cassette tape
To hear song 5, you must play (or fast-forward through) songs 1 to 4. Data is read in order, from start to end.
๐ŸŽต Random access = playlist on a phone
Tap song 5 and it plays right away. You jump directly to any record using its position.
Sequential accessRandom (direct) access
How it readsOne after another, from the beginningJump straight to any position
Functionsfgetc, fgets, fscanf, fread in a loopfseek, ftell, rewind + fread/fwrite
Record sizeCan be different for each recordUsually fixed size (so position = (n-1) ร— size)
Speed to find record nSlow for big filesFast
Updating in the middleHard (usually rewrite the file)Easy (seek and overwrite)
ExampleDiary, log file, poemBank accounts, railway seats, student database

The random access tools

FunctionMeaning
fseek(fp, offset, origin)Move the cursor offset bytes from origin. Returns 0 if OK.
SEEK_SET (0)Count from the start of the file
SEEK_CUR (1)Count from where the cursor is now
SEEK_END (2)Count from the end (use a negative offset to go back)
ftell(fp)Tells the current cursor position in bytes (a long)
rewind(fp)Puts the cursor back at byte 0 (same as fseek(fp, 0, SEEK_SET))
๐ŸŽฏ File cursor playground
The file contains:

fseek(fp, , )

  
Sequential vs random access (table). Explain fseek with its 3 origins, ftell and rewind with an example.

๐Ÿ”Ÿ Sequential access file program: My Diary

A diary. You always write on the next empty page (append mode "a"), and you read from page 1 onwards. To find Wednesday, you have to go past Monday and Tuesday first.

Steps: create the file โ†’ append records one after another โ†’ open in "r" and read in a loop until the end.

Write a C program to create a sequential file, append records to it and display them (like the program below, or with student records using fprintf/fscanf).

File: 09_sequential_access_diary.c

/*
 * Program 9: SEQUENTIAL ACCESS file - My Diary
 * --------------------------------------------
 * Real-life story: A diary. You always WRITE on the next empty page
 * (append), and you READ from page 1, then page 2, then page 3...
 * You can't jump. Like watching a movie from the start.
 *
 * Sequential access = go through the file in order, from start to end.
 */
#include <stdio.h>

void addEntry(const char *day, const char *note) {
    FILE *fp = fopen("diary.txt", "a");     /* "a" = add at the end, keep old pages */
    if (fp == NULL) {
        printf("Could not open diary\n");
        return;
    }
    fprintf(fp, "%s: %s\n", day, note);
    fclose(fp);
}

int main() {
    FILE *fp;
    char line[120];
    int page = 0;

    /* start with an empty diary for this demo */
    fp = fopen("diary.txt", "w");
    if (fp == NULL) return 1;
    fclose(fp);

    /* write entries, one after another */
    addEntry("Monday",    "Learned for loops");
    addEntry("Tuesday",   "Played cricket, we won!");
    addEntry("Wednesday", "Learned about files in C");

    /* read the diary from the first page to the last */
    fp = fopen("diary.txt", "r");
    if (fp == NULL) return 1;
    printf("=== My Diary ===\n");
    while (fgets(line, sizeof(line), fp) != NULL) {
        page++;
        printf("Page %d -> %s", page, line);
    }
    fclose(fp);

    /* search: to find Wednesday we MUST read Monday and Tuesday first */
    fp = fopen("diary.txt", "r");
    if (fp == NULL) return 1;
    page = 0;
    while (fgets(line, sizeof(line), fp) != NULL) {
        page++;
        if (line[0] == 'W') {
            printf("\nFound Wednesday on page %d (had to read %d pages before it)\n",
                   page, page - 1);
            break;
        }
    }
    fclose(fp);

    return 0;
}

Output

=== My Diary ===
Page 1 -> Monday: Learned for loops
Page 2 -> Tuesday: Played cricket, we won!
Page 3 -> Wednesday: Learned about files in C

Found Wednesday on page 3 (had to read 2 pages before it)

1๏ธโƒฃ1๏ธโƒฃ Random access file program: Bank Passbook

The bank keeps all accounts in one file. The cashier wants account 3. She doesn't read accounts 1 and 2. She jumps straight to it, adds the deposit, and saves it back in the same spot.

Every record has the same size, so record N starts at byte (N - 1) ร— sizeof(record).

๐Ÿฆ Where is account N?
Record size (bytes): Account number:

  
Mode "rb+" lets us read and overwrite records without wiping the file. That's the key to updating.
Write a C program to read and update a particular record in a random access file using fseek, fread, fwrite, ftell and rewind.

File: 10_random_access_passbook.c

/*
 * Program 10: RANDOM ACCESS file - Bank Passbook
 * ----------------------------------------------
 * Real-life story: A bank has many account records saved in a file.
 * The cashier wants account number 3. Instead of reading 1, 2, 3...
 * she JUMPS straight to record 3. Like skipping to a song on a playlist!
 *
 *   fseek(fp, offset, from) -> move the file "cursor"
 *        from = SEEK_SET (start), SEEK_CUR (where I am), SEEK_END (end)
 *   ftell(fp)               -> tells where the cursor is (in bytes)
 *   rewind(fp)              -> go back to the very beginning
 *
 * Record N starts at byte: (N - 1) * sizeof(record)
 */
#include <stdio.h>

struct Account {
    int number;
    char name[20];
    float balance;
};

int main() {
    struct Account all[5] = {
        {1, "Anu",    5000},
        {2, "Bala",   1200},
        {3, "Chitra", 9800},
        {4, "Dinesh",  300},
        {5, "Esha",   7100}
    };
    struct Account a;
    FILE *fp;
    int want;
    long size = sizeof(struct Account);

    /* create the bank file */
    fp = fopen("bank.dat", "wb");
    if (fp == NULL) return 1;
    fwrite(all, size, 5, fp);
    fclose(fp);

    fp = fopen("bank.dat", "rb+");           /* read AND write, binary */
    if (fp == NULL) return 1;

    /* 1. Jump straight to account 3 */
    want = 3;
    fseek(fp, (want - 1) * size, SEEK_SET);
    printf("Cursor jumped to byte %ld\n", ftell(fp));
    fread(&a, size, 1, fp);
    printf("Account %d: %s has Rs %.2f\n", a.number, a.name, a.balance);
    printf("After reading, cursor is at byte %ld\n\n", ftell(fp));

    /* 2. Deposit Rs 500 into account 3 and save it back in the same place */
    a.balance = a.balance + 500;
    fseek(fp, (want - 1) * size, SEEK_SET);  /* go back to the start of record 3 */
    fwrite(&a, size, 1, fp);
    printf("Deposited Rs 500 into account %d\n\n", want);

    /* 3. Jump to the LAST record using SEEK_END */
    fseek(fp, -size, SEEK_END);              /* one record back from the end */
    fread(&a, size, 1, fp);
    printf("Last account: %d %s Rs %.2f\n", a.number, a.name, a.balance);

    /* total file size */
    fseek(fp, 0, SEEK_END);
    printf("File size = %ld bytes = %ld records\n\n", ftell(fp), ftell(fp) / size);

    /* 4. rewind and print everything */
    rewind(fp);
    printf("After rewind, cursor is at byte %ld\n", ftell(fp));
    printf("All accounts:\n");
    while (fread(&a, size, 1, fp) == 1) {
        printf("  %d %-7s Rs %.2f\n", a.number, a.name, a.balance);
    }
    fclose(fp);

    return 0;
}

Output

Cursor jumped to byte 56
Account 3: Chitra has Rs 9800.00
After reading, cursor is at byte 84

Deposited Rs 500 into account 3

Last account: 5 Esha Rs 7100.00
File size = 140 bytes = 5 records

After rewind, cursor is at byte 0
All accounts:
  1 Anu     Rs 5000.00
  2 Bala    Rs 1200.00
  3 Chitra  Rs 10300.00
  4 Dinesh  Rs 300.00
  5 Esha    Rs 7100.00

1๏ธโƒฃ2๏ธโƒฃ Command line arguments

At a shop, you can tell the shopkeeper everything while walking in: "Two pencils and one eraser!" You don't wait for questions. Command line arguments are values you give a program when you start it.
int main(int argc, char *argv[])
          โ”‚            โ”‚
          โ”‚            โ””โ”€โ”€ ARGument Vector: an array of strings (the words typed)
          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ARGument Count: how many words, including the program name
โœ‚๏ธ argv splitter: type a command

  
Every argument arrives as a string. "10" is text, not a number. Use atoi() (to int) or atof() (to float) from <stdlib.h> to convert.
argv[0] is always the program's own name. Real arguments start at argv[1]. argv[argc] is NULL.

Program A: add the numbers you type

File: 11_command_line_sum.c

/*
 * Program 11: Command Line Arguments - add numbers
 * ------------------------------------------------
 * Real-life story: Ordering at a shop counter. Instead of the shopkeeper
 * ASKING you questions later (scanf), you tell everything at once
 * while walking in:   ./sum 10 20 30
 *
 *   int main(int argc, char *argv[])
 *   argc -> ARGument Count: how many words were typed (program name included)
 *   argv -> ARGument Vector: the words themselves, as strings
 *
 *   ./sum 10 20 30
 *   argv[0] = "./sum"  argv[1] = "10"  argv[2] = "20"  argv[3] = "30"
 *   argc = 4
 *
 * Run:  gcc 11_command_line_sum.c -o sum
 *       ./sum 10 20 30
 */
#include <stdio.h>
#include <stdlib.h>          /* for atoi: turns "10" (text) into 10 (number) */

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

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

    if (argc < 2) {
        printf("\nPlease give some numbers, like: %s 10 20 30\n", argv[0]);
        return 1;
    }

    for (i = 1; i < argc; i++) {       /* start at 1, because argv[0] is the program name */
        total = total + atoi(argv[i]);
    }
    printf("\nSum of the numbers = %d\n", total);

    return 0;
}

Run commands and output

$ gcc 11_command_line_sum.c -o sum
$ ./sum 10 20 30
argc = 4
argv[0] = "./sum"
argv[1] = "10"
argv[2] = "20"
argv[3] = "30"

Sum of the numbers = 60

$ ./sum
argc = 1
argv[0] = "./sum"

Please give some numbers, like: ./sum 10 20 30

Program B: copy a file named on the command line

File: 12_command_line_copy_file.c

/*
 * Program 12: Command Line Arguments - copy a file
 * ------------------------------------------------
 * Real-life story: A photocopy machine. You say which page to copy
 * and where to put the copy:   ./copy poem.txt poem_copy.txt
 *
 *   argv[1] -> the file to copy FROM (source)
 *   argv[2] -> the file to copy TO   (destination)
 *
 * Run:  gcc 12_command_line_copy_file.c -o copy
 *       ./copy poem.txt poem_copy.txt
 */
#include <stdio.h>

int main(int argc, char *argv[]) {
    FILE *src, *dest;
    int ch;
    long count = 0;

    if (argc != 3) {                         /* need exactly: program, source, destination */
        printf("Usage: %s source_file destination_file\n", argv[0]);
        return 1;
    }

    src = fopen(argv[1], "r");
    if (src == NULL) {
        printf("Cannot open source file \"%s\"\n", argv[1]);
        return 1;
    }

    dest = fopen(argv[2], "w");
    if (dest == NULL) {
        printf("Cannot create destination file \"%s\"\n", argv[2]);
        fclose(src);
        return 1;
    }

    while ((ch = fgetc(src)) != EOF) {       /* copy one character at a time */
        fputc(ch, dest);
        count++;
    }

    fclose(src);
    fclose(dest);
    printf("Copied %ld characters from \"%s\" to \"%s\"\n", count, argv[1], argv[2]);

    return 0;
}

Run commands and output (poem.txt was made by program 5)

$ gcc 12_command_line_copy_file.c -o copy
$ ./copy poem.txt poem_copy.txt
Copied 107 characters from "poem.txt" to "poem_copy.txt"
$ cat poem_copy.txt
Twinkle twinkle little star
How I wonder what you are
Up above the world so high
Like a diamond in the sky

$ ./copy missing.txt x.txt
Cannot open source file "missing.txt"

$ ./copy
Usage: ./copy source_file destination_file
Command line arguments are passed to main as argc (count) and argv (array of strings). argv[0] is the program name. Write a program to copy one file to another using command line arguments (Program B is the classic 16-mark answer).

๐Ÿ“ 2-mark questions & answers

1. What is a file?A file is a named collection of related data stored permanently on secondary storage (disk). It keeps data even after the program ends.
2. Why do we need files?Variables lose their data when the program ends. Files store data permanently, can hold large amounts of data, and let different programs share data.
3. What is a file pointer?A pointer of type FILE * that points to a FILE structure holding information about an opened file (buffer, cursor position, mode). Example: FILE *fp;
4. What is a stream? Name the standard streams.A stream is a sequence of bytes flowing between a program and a device or file. Standard streams: stdin (keyboard), stdout (screen), stderr (screen, errors).
5. Difference between "w" and "a" mode?"w" erases an existing file and writes from the start. "a" keeps the old contents and adds new data at the end. Both create the file if it is missing.
6. What does fopen return if the file cannot be opened?It returns NULL. We must check if (fp == NULL) before using the file.
7. What is EOF?EOF (End Of File) is a constant (usually -1) defined in stdio.h, returned by input functions like fgetc when there is no more data.
8. What is the use of fclose()?It flushes the buffer (actually saves pending data), releases the file, and frees resources. It returns 0 on success, EOF on error.
9. Give two differences between text and binary files.Text files store data as readable ASCII characters; binary files store raw bytes as in memory. Text files may convert newlines; binary files do no conversion and are usually smaller and faster.
10. What is the purpose of fseek()?It moves the file position indicator to a given offset from SEEK_SET (start), SEEK_CUR (current) or SEEK_END (end). Syntax: fseek(fp, offset, origin);
11. What do ftell() and rewind() do?ftell(fp) returns the current position of the file pointer in bytes. rewind(fp) moves the file pointer back to the beginning of the file.
12. What are command line arguments?Values passed to a program when it is run from the terminal. They arrive in main(int argc, char *argv[]): argc is the count and argv is the array of argument strings.
13. What is argv[0]?The name of the program itself (as typed to run it).
14. Sequential vs random access?Sequential access reads records one after another from the beginning. Random access jumps directly to any record using fseek.
15. What do fread and fwrite return?The number of complete items successfully read or written. If it's less than requested, the end of file was reached or an error happened.

๐Ÿ† Mini quiz

๐ŸŽฏ Practice homework

  1. Write a program that counts the characters, words and lines in poem.txt.
  2. Ask the user for 3 friends' names and phone numbers, and save them to contacts.txt using fprintf. Then read them back with fscanf.
  3. Change program 10 so the user types an account number and an amount to withdraw. Don't allow the balance to go below 0.
  4. Write ./upper in.txt out.txt: copy a file but turn every letter into CAPITALS (hint: toupper from <ctype.h>).
  5. Use fseek and ftell to find the size of any file given on the command line.
  6. Write a program that reads students.dat (from program 8) and prints only the students with marks above 80.