Nearby lessons

121 of 124

C - Append to a File

Appending adds to the end of a file without touching what is already there. Learn the "a" and "a+" modes, why every write goes to the end regardless of position, and how appending powers log files and running records.

Appending With "a"

Open with "a", write, close. Whatever was in the file stays:

Example01
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 FILE *fp;
6 
7 /* Create a file with two lines */
8 fp = fopen("log.txt", "w");
9 if (fp == NULL) return 1;
10 fprintf(fp, "Line 1\n");
11 fprintf(fp, "Line 2\n");
12 fclose(fp);
13 
14 /* Append a third - the first two are untouched */
15 fp = fopen("log.txt", "a");
16 if (fp == NULL) return 1;
17 fprintf(fp, "Line 3 (appended)\n");
18 fclose(fp);
19 
20 /* Read it all back */
21 fp = fopen("log.txt", "r");
22 if (fp)
23 {
24 char line[100];
25 while (fgets(line, sizeof line, fp)) printf("%s", line);
26 fclose(fp);
27 }
28 return 0;
29}
Output
Line 1
Line 2
Line 3 (appended)

"a" vs "w" — The Whole Point

In simple words: "w" means "this file is now mine, start fresh". "a" means "keep everything and add to the bottom". Choosing the wrong one is the difference between a log file and an empty file.
"a""w"
Existing contentPreservedErased
Starting positionEnd of fileBeginning
Missing fileCreatedCreated
Can readNoNo
Use forLogs, running recordsFresh output
Example02
CCode Cell
1#include <stdio.h>
2 
3void writeThenShow(const char *mode, const char *text)
4{
5 FILE *fp = fopen("compare.txt", mode);
6 char line[100];
7 
8 if (fp == NULL) return;
9 fprintf(fp, "%s\n", text);
10 fclose(fp);
11 
12 fp = fopen("compare.txt", "r");
13 if (fp == NULL) return;
14 
15 printf("After \"%s\":", mode);
16 while (fgets(line, sizeof line, fp) != NULL)
17 {
18 char *nl = line;
19 while (*nl != '\0' && *nl != '\n') nl++;
20 *nl = '\0'; /* drop the newline */
21 printf(" [%s]", line);
22 }
23 printf("\n");
24 fclose(fp);
25}
26 
27int main()
28{
29 writeThenShow("w", "first");
30 writeThenShow("a", "second");
31 writeThenShow("a", "third");
32 writeThenShow("w", "wiped");
33 return 0;
34}
Output
After "w": [first]
After "a": [first] [second]
After "a": [first] [second] [third]
After "w": [wiped]

Writes Always Go to the End

In append mode, fseek cannot move where you write. The standard requires every write to be repositioned to the end of the file first, so seeking to byte 0 and writing still appends. This surprises people trying to update a record in place — for that you need "r+", not "a+".
Example03
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 FILE *fp;
6 char line[100];
7 
8 fp = fopen("seek.txt", "w");
9 if (fp == NULL) return 1;
10 fprintf(fp, "AAAA\n");
11 fclose(fp);
12 
13 fp = fopen("seek.txt", "a");
14 if (fp == NULL) return 1;
15 
16 fseek(fp, 0, SEEK_SET); /* ask to write at the beginning */
17 fprintf(fp, "BBBB\n"); /* ignored - this goes to the END */
18 fclose(fp);
19 
20 fp = fopen("seek.txt", "r");
21 if (fp)
22 {
23 while (fgets(line, sizeof line, fp)) printf("%s", line);
24 fclose(fp);
25 }
26 return 0;
27}
Output
AAAA
BBBB

"a+" — Append and Read

"a+" adds reading. The read position moves freely; the write position does not:

Example04
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 FILE *fp;
6 char line[100];
7 
8 fp = fopen("records.txt", "w");
9 if (fp == NULL) return 1;
10 fprintf(fp, "Record A\n");
11 fclose(fp);
12 
13 fp = fopen("records.txt", "a+"); /* append AND read */
14 if (fp == NULL) return 1;
15 
16 fprintf(fp, "Record B\n"); /* appended */
17 
18 rewind(fp); /* reading CAN be repositioned */
19 printf("Contents:\n");
20 while (fgets(line, sizeof line, fp)) printf(" %s", line);
21 
22 fclose(fp);
23 return 0;
24}
Output
Contents:
  Record A
  Record B

Appending Creates a Missing File

You never need to check whether the file exists first — "a" handles both cases:

Example05
CCode Cell
1#include <stdio.h>
2 
3void addEntry(const char *text)
4{
5 FILE *fp = fopen("journal.txt", "a"); /* creates it the first time */
6 
7 if (fp == NULL) { perror("journal"); return; }
8 
9 fprintf(fp, "%s\n", text);
10 fclose(fp);
11}
12 
13int main()
14{
15 addEntry("Started the project"); /* file created here */
16 addEntry("Wrote the parser"); /* appended */
17 addEntry("Fixed two bugs"); /* appended */
18 
19 FILE *fp = fopen("journal.txt", "r");
20 char line[100];
21 int n = 1;
22 
23 if (fp)
24 {
25 while (fgets(line, sizeof line, fp)) printf("%d. %s", n++, line);
26 fclose(fp);
27 }
28 return 0;
29}
Output
1. Started the project
2. Wrote the parser
3. Fixed two bugs

A Timestamped Log

The most common real use of append mode. Each run adds to the record instead of replacing it:

Example06
CCode Cell
1#include <stdio.h>
2#include <time.h>
3 
4void logMessage(const char *level, const char *message)
5{
6 FILE *fp = fopen("app.log", "a");
7 time_t now;
8 char stamp[26];
9 
10 if (fp == NULL) return;
11 
12 time(&now);
13 strftime(stamp, sizeof stamp, "%Y-%m-%d %H:%M:%S", localtime(&now));
14 
15 fprintf(fp, "[%s] %-5s %s\n", stamp, level, message);
16 fclose(fp); /* close each time so the log survives a crash */
17}
18 
19int main()
20{
21 logMessage("INFO", "Application started");
22 logMessage("WARN", "Configuration file missing, using defaults");
23 logMessage("ERROR", "Could not reach the database");
24 logMessage("INFO", "Shutting down");
25 
26 printf("Four entries appended to app.log\n");
27 return 0;
28}
Output
Four entries appended to app.log

Appending Structured Records

Appending is not limited to text lines — a CSV or binary record file grows the same way:

Example07
CCode Cell
1#include <stdio.h>
2 
3struct Student { int roll; char name[20]; float marks; };
4 
5void appendStudent(struct Student s)
6{
7 FILE *fp = fopen("students.csv", "a");
8 if (fp == NULL) return;
9 fprintf(fp, "%d,%s,%.1f\n", s.roll, s.name, s.marks);
10 fclose(fp);
11}
12 
13int main()
14{
15 struct Student a = {101, "Rahul", 85.5f};
16 struct Student b = {102, "Priya", 91.0f};
17 char line[100];
18 FILE *fp;
19 
20 /* Write the header once */
21 fp = fopen("students.csv", "w");
22 if (fp) { fprintf(fp, "Roll,Name,Marks\n"); fclose(fp); }
23 
24 appendStudent(a);
25 appendStudent(b);
26 
27 fp = fopen("students.csv", "r");
28 if (fp)
29 {
30 while (fgets(line, sizeof line, fp)) printf("%s", line);
31 fclose(fp);
32 }
33 return 0;
34}
Output
Roll,Name,Marks
101,Rahul,85.5
102,Priya,91.0

Appending Binary Records

Use "ab" for binary. Fixed-size records make the file a simple growing array:

Example08
CCode Cell
1#include <stdio.h>
2 
3struct Reading { int sensor; float value; };
4 
5int main()
6{
7 struct Reading readings[3] = {{1, 23.5f}, {2, 19.8f}, {1, 24.1f}};
8 struct Reading r;
9 FILE *fp;
10 int i, count = 0;
11 
12 /* Append one record at a time */
13 for (i = 0; i < 3; i++)
14 {
15 fp = fopen("readings.dat", "ab");
16 if (fp == NULL) return 1;
17 fwrite(&readings[i], sizeof(struct Reading), 1, fp);
18 fclose(fp);
19 }
20 
21 /* Read them all back */
22 fp = fopen("readings.dat", "rb");
23 if (fp == NULL) return 1;
24 while (fread(&r, sizeof r, 1, fp) == 1)
25 printf("Record %d: sensor %d = %.1f\n", ++count, r.sensor, r.value);
26 fclose(fp);
27 return 0;
28}
Output
Record 1: sensor 1 = 23.5
Record 2: sensor 2 = 19.8
Record 3: sensor 1 = 24.1

Keeping a Log From Growing Forever

An append-only file never shrinks. Check the size occasionally and start fresh when it gets too large:

Example09
CCode Cell
1#include <stdio.h>
2 
3#define MAX_LOG_BYTES 1024
4 
5long fileSize(const char *name)
6{
7 FILE *fp = fopen(name, "rb");
8 long size;
9 
10 if (fp == NULL) return 0; /* no file yet */
11 fseek(fp, 0, SEEK_END);
12 size = ftell(fp);
13 fclose(fp);
14 return size;
15}
16 
17void logWithRotation(const char *message)
18{
19 FILE *fp;
20 
21 if (fileSize("rotating.log") > MAX_LOG_BYTES)
22 {
23 fp = fopen("rotating.log", "w"); /* deliberate truncation */
24 if (fp) { fprintf(fp, "--- log rotated ---\n"); fclose(fp); }
25 }
26 
27 fp = fopen("rotating.log", "a");
28 if (fp) { fprintf(fp, "%s\n", message); fclose(fp); }
29}
30 
31int main()
32{
33 logWithRotation("Entry one");
34 logWithRotation("Entry two");
35 printf("Current size: %ld bytes\n", fileSize("rotating.log"));
36 return 0;
37}
Output
Current size: 20 bytes

Common Mistakes

  • Using "w" when you meant "a" — the existing contents are erased at fopen.
  • Expecting fseek to work for writing — in append mode every write goes to the end.
  • Trying to read in mode "a" — use "a+".
  • Forgetting the newlinefprintf(fp, "text") runs entries together on one line.
  • Not closing between appends — a crash then loses the buffered entries.
  • Letting a log grow unbounded — rotate it by size or by date.
  • Expecting "a+" to update records in place — you need "r+" for that.
Trainer's Note: the "open, write one line, close" pattern used in the log examples looks wasteful, and for a hot loop it is. But it guarantees every entry is on disk before the next line of your program runs — which is exactly what you want from a log, because the entry you most need is the one written just before the crash.
📝 Key Takeaways
  • Mode "a" opens for appending and creates the file if it is missing.
  • Every write in append mode goes to the end, whatever fseek says.
  • Unlike "w", appending never destroys existing content.
  • "a+" allows reading as well, but writes still land at the end.
  • Appending is the natural mode for logs and accumulating records.

🧠 Test Your Knowledge

4 Questions
Progress: 0 / 4