Nearby lessons

117 of 124

C - Open a File

fopen connects your program to a file and hands back a FILE *. Learn the full syntax, every mode string, what a file pointer really holds, and how to diagnose an open that fails.

The Syntax

Two arguments: the filename and the mode. The return value is a pointer to a FILE structure, or NULL:

Example01
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 FILE *fp; /* the file pointer */
6 
7 fp = fopen("notes.txt", "w"); /* filename, mode */
8 
9 if (fp == NULL)
10 {
11 printf("Could not open the file\n");
12 return 1;
13 }
14 
15 fprintf(fp, "The file is open\n");
16 fclose(fp); /* always close */
17 
18 printf("Opened, written and closed\n");
19 return 0;
20}
Output
Opened, written and closed

The Six Basic Modes

Three letters and an optional +. The + adds the missing direction:

ModeReadWriteMissing fileExisting fileStarts at
"r"YesNoFailsKeptBeginning
"w"NoYesCreatedTruncatedBeginning
"a"NoYesCreatedKeptEnd
"r+"YesYesFailsKeptBeginning
"w+"YesYesCreatedTruncatedBeginning
"a+"YesYesCreatedKeptReads anywhere, writes at end
Example02
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 FILE *fp;
6 
7 /* Set up a file to demonstrate with */
8 fp = fopen("demo.txt", "w");
9 if (fp) { fprintf(fp, "Line 1\n"); fclose(fp); }
10 
11 /* "a" keeps what is there and adds to the end */
12 fp = fopen("demo.txt", "a");
13 if (fp) { fprintf(fp, "Line 2\n"); fclose(fp); }
14 
15 /* "r" reads it back */
16 fp = fopen("demo.txt", "r");
17 if (fp)
18 {
19 char line[100];
20 while (fgets(line, sizeof line, fp)) printf("%s", line);
21 fclose(fp);
22 }
23 return 0;
24}
Output
Line 1
Line 2

Text vs Binary

Adding b requests binary mode. On Windows, text mode translates \n to a two-byte line ending on write and back on read; binary mode passes bytes through untouched:

Example03
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 FILE *fp;
6 int numbers[3] = {100, 200, 300};
7 
8 /* Text mode - human-readable */
9 fp = fopen("numbers.txt", "w");
10 if (fp)
11 {
12 fprintf(fp, "%d %d %d\n", numbers[0], numbers[1], numbers[2]);
13 fclose(fp);
14 }
15 
16 /* Binary mode - raw bytes, exact round-trip */
17 fp = fopen("numbers.bin", "wb");
18 if (fp)
19 {
20 fwrite(numbers, sizeof(int), 3, fp);
21 fclose(fp);
22 }
23 
24 printf("Text : 12 bytes of digits and spaces\n");
25 printf("Binary: %zu bytes of raw ints\n", 3 * sizeof(int));
26 return 0;
27}
Output
Text : 12 bytes of digits and spaces
Binary: 12 bytes of raw ints

Always Use Binary Mode for Binary Data

Writing a struct with fwrite in text mode corrupts it on Windows. Any byte that happens to equal 0x0A gets silently expanded to two bytes on the way out, so the file you read back does not match the one you wrote. On Linux and macOS text and binary mode are identical — which is exactly why this bug survives testing and appears only on a customer's Windows machine.
Example04
CCode Cell
1#include <stdio.h>
2 
3struct Record { int id; float value; };
4 
5int main()
6{
7 struct Record out = {10, 3.14f}, in = {0, 0.0f};
8 FILE *fp;
9 
10 /* Correct: "wb" and "rb" */
11 fp = fopen("safe.dat", "wb");
12 if (fp == NULL) return 1;
13 fwrite(&out, sizeof out, 1, fp);
14 fclose(fp);
15 
16 fp = fopen("safe.dat", "rb");
17 if (fp == NULL) return 1;
18 fread(&in, sizeof in, 1, fp);
19 fclose(fp);
20 
21 printf("Wrote: id=%d value=%.2f\n", out.id, out.value);
22 printf("Read : id=%d value=%.2f\n", in.id, in.value);
23 return 0;
24}
Output
Wrote: id=10 value=3.14
Read : id=10 value=3.14

What a FILE Pointer Actually Is

In simple words: a FILE * is not the file and does not contain its data. It points to a small bookkeeping structure the library maintains — a memory buffer, your current position, the end-of-file and error flags, and the operating system's handle. Every fprintf and fgets works through it.
Example05
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 FILE *fp = fopen("position.txt", "w+");
6 if (fp == NULL) return 1;
7 
8 printf("Position after open : %ld\n", ftell(fp));
9 
10 fprintf(fp, "Hello");
11 printf("After writing 5 chars: %ld\n", ftell(fp));
12 
13 rewind(fp); /* back to the start */
14 printf("After rewind : %ld\n", ftell(fp));
15 
16 fseek(fp, 3, SEEK_SET); /* jump to byte 3 */
17 printf("After fseek to 3 : %ld\n", ftell(fp));
18 
19 fclose(fp);
20 return 0;
21}
Output
Position after open : 0
After writing 5 chars: 5
After rewind        : 0
After fseek to 3    : 3

Why an Open Fails

Several distinct causes produce the same NULL. perror tells you which one:

CauseTypical message
File does not exist (mode "r")No such file or directory
A directory in the path is missingNo such file or directory
No permissionPermission denied
The name is a directoryIs a directory
Too many files already openToo many open files
Disk full or read-onlyNo space left on device
Example06
CCode Cell
1#include <stdio.h>
2#include <errno.h>
3#include <string.h>
4 
5int main()
6{
7 FILE *fp = fopen("does_not_exist.txt", "r");
8 
9 if (fp == NULL)
10 {
11 perror("fopen"); /* prefix: reason */
12 printf("errno = %d\n", errno);
13 printf("strerror = %s\n", strerror(errno));
14 return 1;
15 }
16 
17 fclose(fp);
18 return 0;
19}
Output
fopen: No such file or directory
errno    = 2
strerror = No such file or directory

Opening Several Files

Each open file needs its own FILE *. Close each one, and close what you opened even when a later open fails:

Example07
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 FILE *in, *out;
6 int ch; /* int, not char - EOF needs it */
7 
8 in = fopen("source.txt", "w"); /* create some input */
9 if (in == NULL) return 1;
10 fprintf(in, "Copy me\n");
11 fclose(in);
12 
13 in = fopen("source.txt", "r");
14 if (in == NULL) { perror("source"); return 1; }
15 
16 out = fopen("copy.txt", "w");
17 if (out == NULL)
18 {
19 perror("copy");
20 fclose(in); /* do not leak the first */
21 return 1;
22 }
23 
24 while ((ch = fgetc(in)) != EOF) fputc(ch, out);
25 
26 fclose(in);
27 fclose(out);
28 printf("Copied source.txt to copy.txt\n");
29 return 0;
30}
Output
Copied source.txt to copy.txt

The Three Streams You Never Open

Three FILE * values are open before main starts. They behave like any other stream:

Example08
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 /* printf(...) is exactly fprintf(stdout, ...) */
6 fprintf(stdout, "This goes to standard output\n");
7 fprintf(stderr, "This goes to standard error\n");
8 
9 printf("\nRedirect them at the shell:\n");
10 printf(" ./program > out.txt captures stdout only\n");
11 printf(" ./program 2> err.txt captures stderr only\n");
12 
13 /* Error messages belong on stderr so they still appear
14 when stdout has been redirected to a file. */
15 return 0;
16}
Output
This goes to standard output
This goes to standard error

Redirect them at the shell:
  ./program > out.txt      captures stdout only
  ./program 2> err.txt     captures stderr only

Filenames From the User

Take the name at run time — from a prompt or the command line — rather than hard-coding it:

Example09
CCode Cell
1#include <stdio.h>
2#include <string.h>
3 
4int main(int argc, char *argv[])
5{
6 char filename[100];
7 FILE *fp;
8 
9 if (argc > 1)
10 strncpy(filename, argv[1], sizeof(filename) - 1);
11 else
12 {
13 printf("Enter a filename: ");
14 if (fgets(filename, sizeof filename, stdin) == NULL) return 1;
15 filename[strcspn(filename, "\n")] = '\0'; /* strip the newline */
16 }
17 filename[sizeof(filename) - 1] = '\0';
18 
19 fp = fopen(filename, "r");
20 if (fp == NULL) { perror(filename); return 1; }
21 
22 printf("Opened %s successfully\n", filename);
23 fclose(fp);
24 return 0;
25}
Output
Enter a filename: demo.txt
Opened demo.txt successfully

Common Mistakes

  • Skipping the NULL check — the first read or write then crashes.
  • Using "w" when you meant "a" — the existing contents are erased.
  • Text mode for binary data — corrupts bytes on Windows.
  • Forgetting fclose — leaks a handle and may lose buffered writes.
  • Reusing one FILE * for two files — the first handle is lost and leaked.
  • Single backslashes in Windows paths"C:\temp" contains a tab character.
  • Reading from a file opened "w" — the operation fails silently unless you check.
Trainer's Note: pick the mode by asking two questions in order — "must the file already exist?" and "am I replacing it or adding to it?" "r" for must-exist, "w" for replace, "a" for add. The + and b are refinements you add afterwards.
📝 Key Takeaways
  • FILE *fp = fopen("name", "mode"); — always check the result for NULL.
  • The mode string decides read/write, truncate/append, and text/binary.
  • A FILE * holds the buffer, position and error flags — not the file contents.
  • perror and strerror explain why an open failed.
  • Every successful fopen needs a matching fclose.

🧠 Test Your Knowledge

4 Questions
Progress: 0 / 4