Educational Blog

How to Write to a File in C

Learn how to open, write, append, and close files in C.

Writing to a file in C is one of those skills that looks simple in a small example and becomes much more useful once you understand the moving parts. The standard library gives you a portable file API, and the core workflow is straightforward: open a file, write data, check for errors, and close the file. Once you have that sequence down, you can build everything from loggers and exporters to small tools that generate reports or save program output.

The main thing to remember is that C does not hide file handling behind a large abstraction layer. You work with streams, file modes, and explicit error checking. That can feel old-school at first, but it gives you direct control and makes it easy to understand exactly what your program is doing.

The basic flow

A typical file-writing program follows this pattern:

  1. Include the right header, usually stdio.h.
  2. Open a file with fopen().
  3. Write text or binary data with fprintf(), fputs(), fwrite(), or related functions.
  4. Check whether each operation succeeded.
  5. Close the file with fclose().

Here is the smallest practical example:

int main(void) {
    FILE *file = fopen("output.txt", "w");
    if (file == NULL) {
        perror("fopen");
        return 1;
    }

    fprintf(file, "Hello from C!
");

    if (fclose(file) != 0) {
        perror("fclose");
        return 1;
    }

    return 0;
}

That program creates output.txt if it does not exist, or truncates it if it does. The key choice here is the mode string "w", which means write mode. If the file already exists, its contents are cleared before writing begins.

Choosing the right open mode

The mode string passed to fopen() determines how the file behaves. Picking the wrong one is a common source of bugs, especially when you are first learning C file I/O.

ModeMeaningTypical use
"r"Open for readingRead an existing file
"w"Open for writing, truncate if existsCreate or overwrite output
"a"Open for appendingAdd logs or records to the end
"r+"Read and write without truncatingUpdate an existing file in place
"w+"Read and write, truncate if existsRebuild a file from scratch
"a+"Read and appendAppend while still allowing reads

For writing to a file in C, "w" and "a" are the most common choices. Use "w" when you want a fresh result each run. Use "a" when you want to preserve prior data and add more at the end, such as in a log file.

Text output with fprintf and fputs

If you are writing human-readable content, fprintf() is usually the most flexible option because it works like printf() but sends output to a file instead of the terminal.

fprintf(file, "Name: %s
", name);
fprintf(file, "Score: %d
", score);

You can also use fputs() for plain strings when formatting is not needed.

fputs("First line
", file);
fputs("Second line
", file);

A good rule is this: use fprintf() when you need formatting, and fputs() when you already have a string exactly as you want it.

When to prefer printf-style output

fprintf() is helpful when you are building structured text files, CSV-like output, reports, or debug dumps. You can combine labels, values, and line breaks in one call. That often makes the code easier to read than multiple smaller writes.

For example:

fprintf(file, "%s,%d,%.2f
", product, quantity, price);

That line writes a comma-separated record with a string, an integer, and a floating-point value.

Writing binary data with fwrite

Not every file is text. If you are storing raw structures, images, buffers, or other binary data, use fwrite().

size_t written = fwrite(buffer, 1, buffer_size, file);
if (written != buffer_size) {
    perror("fwrite");
}

fwrite() is useful when the exact bytes matter. It writes blocks of memory directly to the file, which is efficient and precise. The tradeoff is portability: raw binary layouts may depend on compiler, architecture, padding, or endianness if you write complex structures directly.

If you are new to C, the safest habit is to write binary data deliberately, with a clear file format, instead of dumping whole structs blindly unless you fully control the environment.

Checking errors properly

Good file-writing code does not stop at ?the program compiled.? You need to check for failure at each important step.

Common failure points include:

  • fopen() returning NULL
  • fprintf() or fputs() failing because of disk problems
  • fwrite() writing fewer bytes than expected
  • fclose() reporting an error during flush

A careful pattern looks like this:

#include <stdio.h>

int main(void) {
    FILE *file = fopen("report.txt", "w");
    if (!file) {
        perror("fopen");
        return 1;
    }

    if (fprintf(file, "Report line 1
") < 0) {
        perror("fprintf");
        fclose(file);
        return 1;
    }

    if (fclose(file) != 0) {
        perror("fclose");
        return 1;
    }

    return 0;
}

That extra checking matters when you are writing anything important. If the filesystem fills up or the destination is unavailable, you want a clear failure instead of silently losing data.

Appending instead of overwriting

Sometimes the right answer is not to replace the file but to extend it. That is what append mode is for.

FILE *log = fopen("app.log", "a");
if (!log) {
    perror("fopen");
    return 1;
}

fprintf(log, "Application started
");
fclose(log);

In append mode, new writes go to the end of the file. This is ideal for logs, audit trails, event histories, and simple records where each run should preserve earlier output.

A subtle point: append mode protects you from accidentally overwriting past data, but it does not magically solve concurrency or log rotation. If multiple processes may write to the same file, you need a more careful design.

Buffered output and why close matters

C file output is buffered. That means your program may collect data in memory before physically writing it to disk. Buffering improves performance, but it also means that fclose() is not a formality. It flushes pending data and releases the file handle.

If you exit too early, or forget to close a file, buffered data may never be written. For small programs it can look like everything worked until you inspect the file and notice it is incomplete.

If you need to force a flush before closing, use fflush(file), but do that only when necessary. In most cases, fclose() is enough.

Practical patterns you will use often

Here are a few common writing patterns that show up again and again in real C code:

  • Write a single text line to a report file.
  • Append timestamped log messages to a log file.
  • Dump a table of computed values to CSV-like output.
  • Save a buffer of bytes to a binary file.
  • Serialize a simple custom record format for later reading.

A nice way to think about file output in C is as a tool for making your program useful beyond the terminal. When a program can save its results, you can chain it into other tools, inspect its output later, or compare multiple runs.

Common mistakes to avoid

A few bugs come up frequently when writing to files in C:

  1. Forgetting to test whether fopen() failed.
  2. Using the wrong open mode and accidentally truncating the file.
  3. Writing to the wrong stream because you passed the wrong FILE *.
  4. Ignoring partial writes from fwrite().
  5. Forgetting to close the file after writing.
  6. Assuming binary data written from a struct will be portable everywhere.

If you avoid those mistakes, you are already ahead of many beginner examples. Most file-writing bugs are not complicated; they come from skipping the boring checks.

A small reusable helper

Once you write a few file programs, it helps to wrap repeated logic into a helper function. For example, you might build a tiny helper for writing a text block:

#include <stdio.h>

int write_message(const char *path, const char *message) {
    FILE *file = fopen(path, "w");
    if (!file) {
        return 0;
    }

    if (fputs(message, file) == EOF) {
        fclose(file);
        return 0;
    }

    if (fclose(file) != 0) {
        return 0;
    }

    return 1;
}

That kind of helper makes your application code cleaner. It also gives you one place to improve error handling later.

Bottom line

Writing to a file in C is mostly about being explicit. Open the file with the right mode, write carefully, check results, and close the handle every time. Once that becomes routine, you can move from toy examples to real programs that export data, store logs, and generate files reliably.

If you only remember one thing, remember this: file output in C is not difficult, but it rewards discipline. A few extra checks make the difference between a program that appears to work and one that really does.

Written by

c-double.com Editorial Team

Editorial team

c-double.com publishes practical how-to guides and educational articles with clear steps and useful context.