Docs

Writing Files

Writing to Files in C

Table of Contents

  1. •Introduction
  2. •Opening Files for Writing
  3. •Character Output Functions
  4. •String Output Functions
  5. •Formatted Output
  6. •Block Output
  7. •File Positioning for Writing
  8. •Appending to Files
  9. •Error Handling
  10. •Flushing Output
  11. •Best Practices
  12. •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

ModeDescriptionCreatesTruncates
"w"Write (text mode)YesYes
"wb"Write (binary mode)YesYes
"a"Append (text mode)YesNo
"ab"Append (binary mode)YesNo
"r+"Read/Write (existing file)NoNo
"w+"Read/Write (new file)YesYes
"a+"Read/AppendYesNo

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

SpecifierTypeExample
%d, %iint42
%uunsigned int42
%ldlong1234567890
%ffloat/double3.140000
%escientific3.14e+00
%.2f2 decimals3.14
%sstringHello
%ccharA
%x, %Xhex2a, 2A
%ppointer0x7fff...
%%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

ErrorCause
EACCESPermission denied
ENOSPCNo space left on device
EROFSRead-only file system
EIOI/O error
EFBIGFile 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

FunctionPurposeReturns
fputc()Write single characterchar or EOF
fputs()Write string≥0 or EOF
fprintf()Formatted outputchars written
fwrite()Write binary blockelements written
fflush()Force buffer write0 or EOF
fseek()Position for writing0 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

Writing Files - C Programming Tutorial | DeepML