Docs
Writing Files
Writing to Files in C
Table of Contents
- •Introduction
- •Opening Files for Writing
- •Character Output Functions
- •String Output Functions
- •Formatted Output
- •Block Output
- •File Positioning for Writing
- •Appending to Files
- •Error Handling
- •Flushing Output
- •Best Practices
- •Summary
Introduction
Writing to files is essential for:
- •Data persistence: Saving program output and user data
- •Logging: Recording program events and errors
- •Configuration: Creating and updating config files
- •Reports: Generating text, CSV, or formatted reports
- •Interprocess communication: Writing data for other programs
Key Concepts
FILE *fp = fopen("file.txt", "w"); // Open for writing
if (fp != NULL) {
// Write operations
fclose(fp); // Always close the file
}
Opening Files for Writing
Write Modes
| Mode | Description | Creates | Truncates |
|---|---|---|---|
"w" | Write (text mode) | Yes | Yes |
"wb" | Write (binary mode) | Yes | Yes |
"a" | Append (text mode) | Yes | No |
"ab" | Append (binary mode) | Yes | No |
"r+" | Read/Write (existing file) | No | No |
"w+" | Read/Write (new file) | Yes | Yes |
"a+" | Read/Append | Yes | No |
Basic File Opening
#include <stdio.h>
int main() {
// Open for writing (creates new or truncates existing)
FILE *fp = fopen("output.txt", "w");
if (fp == NULL) {
perror("Failed to open file");
return 1;
}
// Write operations here
fclose(fp);
return 0;
}
Creating New File
FILE *fp = fopen("newfile.txt", "w");
// If file exists: contents are erased
// If file doesn't exist: new file is created
Appending to File
FILE *fp = fopen("log.txt", "a");
// If file exists: writes go to end
// If file doesn't exist: new file is created
Character Output Functions
fputc() - Write Single Character
int fputc(int character, FILE *stream);
Returns character written on success, EOF on error.
#include <stdio.h>
int main() {
FILE *fp = fopen("chars.txt", "w");
if (fp == NULL) return 1;
// Write individual characters
fputc('H', fp);
fputc('e', fp);
fputc('l', fp);
fputc('l', fp);
fputc('o', fp);
fputc('\n', fp);
fclose(fp);
return 0;
}
// File contains: Hello
putc() - Macro Version
int putc(int character, FILE *stream);
Similar to fputc but may be implemented as a macro (faster but evaluates stream multiple times).
putc('X', fp); // Same usage as fputc
Writing Character Arrays
void writeChars(FILE *fp, const char *str) {
while (*str) {
if (fputc(*str++, fp) == EOF) {
perror("Write error");
break;
}
}
}
String Output Functions
fputs() - Write String
int fputs(const char *str, FILE *stream);
Writes string without the null terminator. Returns non-negative on success, EOF on error.
#include <stdio.h>
int main() {
FILE *fp = fopen("strings.txt", "w");
if (fp == NULL) return 1;
fputs("First line\n", fp);
fputs("Second line\n", fp);
fputs("No newline here", fp);
fclose(fp);
return 0;
}
/* File contains:
First line
Second line
No newline here
*/
puts() vs fputs()
// puts() - writes to stdout and adds newline
puts("Hello"); // Outputs: Hello\n
// fputs() - writes to file without adding newline
fputs("Hello", fp); // Writes just: Hello
fputs("Hello\n", fp); // Writes: Hello\n
Writing Multiple Strings
const char *lines[] = {
"Line 1",
"Line 2",
"Line 3"
};
FILE *fp = fopen("output.txt", "w");
for (int i = 0; i < 3; i++) {
fputs(lines[i], fp);
fputc('\n', fp);
}
fclose(fp);
Formatted Output
fprintf() - Formatted File Output
int fprintf(FILE *stream, const char *format, ...);
Returns number of characters written, negative on error.
#include <stdio.h>
int main() {
FILE *fp = fopen("data.txt", "w");
if (fp == NULL) return 1;
// Write formatted data
fprintf(fp, "Name: %s\n", "John Doe");
fprintf(fp, "Age: %d\n", 25);
fprintf(fp, "Salary: $%.2f\n", 75000.50);
fprintf(fp, "ID: %08d\n", 42);
fclose(fp);
return 0;
}
/* File contains:
Name: John Doe
Age: 25
Salary: $75000.50
ID: 00000042
*/
Format Specifiers Review
| Specifier | Type | Example |
|---|---|---|
%d, %i | int | 42 |
%u | unsigned int | 42 |
%ld | long | 1234567890 |
%f | float/double | 3.140000 |
%e | scientific | 3.14e+00 |
%.2f | 2 decimals | 3.14 |
%s | string | Hello |
%c | char | A |
%x, %X | hex | 2a, 2A |
%p | pointer | 0x7fff... |
%% | literal % | % |
Field Width and Precision
fprintf(fp, "|%10s|%-10s|\n", "right", "left");
// | right|left |
fprintf(fp, "|%10d|%-10d|\n", 42, 42);
// | 42|42 |
fprintf(fp, "|%10.2f|%-10.2f|\n", 3.14159, 3.14159);
// | 3.14|3.14 |
Writing CSV Data
void writeCSV(FILE *fp, const char *name, int age, float gpa) {
fprintf(fp, "%s,%d,%.2f\n", name, age, gpa);
}
int main() {
FILE *fp = fopen("students.csv", "w");
if (fp == NULL) return 1;
// Header
fprintf(fp, "Name,Age,GPA\n");
// Data
writeCSV(fp, "Alice", 20, 3.8);
writeCSV(fp, "Bob", 22, 3.5);
writeCSV(fp, "Carol", 21, 3.9);
fclose(fp);
return 0;
}
Block Output
fwrite() - Write Data Block
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
Returns number of elements written.
#include <stdio.h>
int main() {
int numbers[] = {10, 20, 30, 40, 50};
size_t count = sizeof(numbers) / sizeof(numbers[0]);
FILE *fp = fopen("numbers.bin", "wb");
if (fp == NULL) return 1;
size_t written = fwrite(numbers, sizeof(int), count, fp);
printf("Wrote %zu integers\n", written);
fclose(fp);
return 0;
}
Writing Structures
struct Person {
char name[50];
int age;
float salary;
};
int main() {
struct Person people[] = {
{"Alice", 30, 75000.0},
{"Bob", 25, 55000.0}
};
FILE *fp = fopen("people.dat", "wb");
if (fp == NULL) return 1;
size_t written = fwrite(people, sizeof(struct Person), 2, fp);
printf("Wrote %zu records\n", written);
fclose(fp);
return 0;
}
fwrite Error Checking
size_t count = 10;
size_t written = fwrite(buffer, sizeof(int), count, fp);
if (written != count) {
if (ferror(fp)) {
perror("Write error");
}
}
File Positioning for Writing
fseek() - Set Position
int fseek(FILE *stream, long offset, int whence);
// Write at specific position
FILE *fp = fopen("data.bin", "r+b"); // Read/write mode
// Move to byte 100
fseek(fp, 100, SEEK_SET);
fwrite(&value, sizeof(int), 1, fp);
// Move back 4 bytes and overwrite
fseek(fp, -4, SEEK_CUR);
fwrite(&newValue, sizeof(int), 1, fp);
Overwriting Data
#include <stdio.h>
#include <string.h>
int main() {
// Create initial file
FILE *fp = fopen("test.txt", "w");
fprintf(fp, "AAAA BBBB CCCC");
fclose(fp);
// Open for updating
fp = fopen("test.txt", "r+");
// Overwrite middle section
fseek(fp, 5, SEEK_SET);
fprintf(fp, "XXXX");
fclose(fp);
// File now contains: AAAA XXXX CCCC
return 0;
}
Appending to Files
Append Mode ("a")
FILE *fp = fopen("log.txt", "a");
// All writes go to end of file
// fseek doesn't affect write position
fprintf(fp, "[%s] Log entry\n", timestamp);
fclose(fp);
Log File Example
#include <stdio.h>
#include <time.h>
void logMessage(const char *filename, const char *message) {
FILE *fp = fopen(filename, "a");
if (fp == NULL) return;
// Get timestamp
time_t now = time(NULL);
char *timestamp = ctime(&now);
timestamp[strlen(timestamp) - 1] = '\0'; // Remove newline
fprintf(fp, "[%s] %s\n", timestamp, message);
fclose(fp);
}
int main() {
logMessage("app.log", "Application started");
logMessage("app.log", "Processing data...");
logMessage("app.log", "Application finished");
return 0;
}
Append vs Write Mode
// Write mode - truncates file
FILE *fp1 = fopen("file.txt", "w");
fprintf(fp1, "New content");
fclose(fp1);
// File contains only: New content
// Append mode - preserves content
FILE *fp2 = fopen("file.txt", "a");
fprintf(fp2, " More content");
fclose(fp2);
// File contains: New content More content
Error Handling
Checking for Errors
#include <stdio.h>
#include <errno.h>
int main() {
FILE *fp = fopen("output.txt", "w");
if (fp == NULL) {
perror("fopen failed");
return 1;
}
if (fprintf(fp, "Test data") < 0) {
perror("fprintf failed");
fclose(fp);
return 1;
}
if (fclose(fp) != 0) {
perror("fclose failed");
return 1;
}
return 0;
}
ferror() - Check Stream Error
size_t written = fwrite(data, sizeof(int), count, fp);
if (written != count) {
if (ferror(fp)) {
printf("Error writing to file\n");
clearerr(fp); // Clear error indicator
}
}
Common Write Errors
| Error | Cause |
|---|---|
| EACCES | Permission denied |
| ENOSPC | No space left on device |
| EROFS | Read-only file system |
| EIO | I/O error |
| EFBIG | File too large |
Flushing Output
fflush() - Force Write
int fflush(FILE *stream);
Forces buffered data to be written immediately.
FILE *fp = fopen("important.txt", "w");
fprintf(fp, "Critical data");
fflush(fp); // Ensure data is written to disk NOW
// Continue processing...
When to Flush
// 1. Before reading from same file opened for update
FILE *fp = fopen("data.txt", "r+");
fprintf(fp, "data");
fflush(fp); // Flush before reading
// ... read operations ...
// 2. Before long operations
fprintf(fp, "Starting long operation...\n");
fflush(fp); // Make visible immediately
longOperation();
// 3. Before forking or exec
fflush(stdout);
fflush(stderr);
pid_t pid = fork();
Buffering Modes
// Set line buffering
setvbuf(fp, NULL, _IOLBF, 0);
// Set full buffering
setvbuf(fp, NULL, _IOFBF, 8192);
// Set no buffering
setvbuf(fp, NULL, _IONBF, 0);
Best Practices
1. Always Check Return Values
FILE *fp = fopen("file.txt", "w");
if (fp == NULL) {
// Handle error
return -1;
}
2. Use Appropriate Mode
// Text files
FILE *text = fopen("file.txt", "w");
// Binary files
FILE *binary = fopen("file.bin", "wb");
// Don't mix them!
3. Close Files in All Code Paths
FILE *fp = fopen("file.txt", "w");
if (fp == NULL) return -1;
if (someCondition) {
fclose(fp); // Don't forget!
return -2;
}
// ... more code ...
fclose(fp);
return 0;
4. Use Temporary Files Safely
#include <stdio.h>
int main() {
// Create temporary file
FILE *tmp = tmpfile();
if (tmp == NULL) return 1;
fprintf(tmp, "Temporary data");
// File is automatically deleted when closed
fclose(tmp);
return 0;
}
5. Atomic Writes (Write-Then-Rename)
// Safe file update pattern
void safeUpdate(const char *filename, const char *data) {
char tempname[256];
snprintf(tempname, sizeof(tempname), "%s.tmp", filename);
FILE *fp = fopen(tempname, "w");
if (fp == NULL) return;
fprintf(fp, "%s", data);
fclose(fp);
// Atomic rename
rename(tempname, filename);
}
Common Patterns
Pattern 1: Writing Configuration File
void writeConfig(const char *filename) {
FILE *fp = fopen(filename, "w");
if (fp == NULL) return;
fprintf(fp, "# Configuration File\n");
fprintf(fp, "# Generated automatically\n\n");
fprintf(fp, "setting1 = value1\n");
fprintf(fp, "setting2 = value2\n");
fprintf(fp, "enabled = true\n");
fclose(fp);
}
Pattern 2: Writing Array to File
void writeIntArray(const char *filename, int *arr, int size) {
FILE *fp = fopen(filename, "wb");
if (fp == NULL) return;
// Write size first
fwrite(&size, sizeof(int), 1, fp);
// Write array
fwrite(arr, sizeof(int), size, fp);
fclose(fp);
}
Pattern 3: Formatted Table Output
void writeTable(FILE *fp, const char *headers[], int cols,
const char *data[][10], int rows) {
// Write header
for (int i = 0; i < cols; i++) {
fprintf(fp, "| %-15s ", headers[i]);
}
fprintf(fp, "|\n");
// Write separator
for (int i = 0; i < cols; i++) {
fprintf(fp, "|%s", "-----------------");
}
fprintf(fp, "|\n");
// Write data rows
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
fprintf(fp, "| %-15s ", data[r][c]);
}
fprintf(fp, "|\n");
}
}
Summary
Key Functions
| Function | Purpose | Returns |
|---|---|---|
fputc() | Write single character | char or EOF |
fputs() | Write string | ≥0 or EOF |
fprintf() | Formatted output | chars written |
fwrite() | Write binary block | elements written |
fflush() | Force buffer write | 0 or EOF |
fseek() | Position for writing | 0 or non-zero |
Mode Summary
"w" - Write text (truncate)
"wb" - Write binary (truncate)
"a" - Append text
"ab" - Append binary
"r+" - Read/Write existing
"w+" - Read/Write new
"a+" - Read/Append
Error Checking Pattern
FILE *fp = fopen(filename, mode);
if (fp == NULL) { /* handle error */ }
if (write_operation < 0 || ferror(fp)) {
perror("Write failed");
}
if (fclose(fp) != 0) { /* handle error */ }
Navigation
| Previous | Up | Next |
|---|---|---|
| Reading Files | File Handling | Binary Files |