Argumen kedua fopen adalah mode—sebuah string yang menentukan apakah kita ingin membaca, menulis, menambahkan, atau kombinasi dari ketiganya. Memilih mode yang salah adalah salah satu bug paling umum dalam file I/O: membuka file dengan "w" ketika kita maksudkan "r+" akan menghapus seluruh isi file tanpa peringatan. Pada chapter ini kita akan membedah seluruh kombinasi mode yang didukung C11, melihat apa yang terjadi pada masing-masing mode, dan memahami kapan binary mode "rb"/"wb" berbeda dari text mode "r"/"w".
Tabel Lengkap Mode fopen #
ISO/IEC 9899 §7.21.5.3 mendefinisikan kombinasi mode berikut:
| Mode | File harus ada? | Awal posisi | Operasi | Truncate? |
|---|---|---|---|---|
"r" | Ya | Awal | Baca saja | Tidak |
"w" | Tidak (buat baru) | Awal | Tulis saja | Ya |
"a" | Tidak (buat baru) | Akhir | Tulis (append) | Tidak |
"r+" | Ya | Awal | Baca + tulis | Tidak |
"w+" | Tidak (buat baru) | Awal | Baca + tulis | Ya |
"a+" | Tidak (buat baru) | Akhir | Baca + tulis (tulis selalu append) | Tidak |
Karakter "b" ("rb", "wb", "ab", "r+b", "w+b", "a+b") menandakan binary mode. Karakter "x" (sejak C11, ditambahkan setelah "w"/"w+") membuat fopen gagal bila file sudah ada—berguna untuk mencegah overwrite tidak sengaja.
Demo Semua Mode Dasar #
Mari kita uji setiap mode dasar untuk melihat efeknya pada file yang sama.
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void) {
/* 1) Mode "w": tulis dari awal (truncate bila ada) */
FILE *f = fopen("/tmp/test_modes.txt", "w");
if (!f) { perror("w"); return 1; }
fputs("baris awal\n", f);
fclose(f);
/* 2) Mode "r": baca, file harus ada */
f = fopen("/tmp/test_modes.txt", "r");
if (f) {
char buf[64];
if (fgets(buf, sizeof(buf), f)) printf("r baca: %s", buf);
fclose(f);
}
/* 3) Mode "a": append di akhir */
f = fopen("/tmp/test_modes.txt", "a");
if (f) {
fputs("baris tambahan\n", f);
fclose(f);
}
/* 4) Mode "r+": baca dan tulis tanpa truncate */
f = fopen("/tmp/test_modes.txt", "r+");
if (f) {
fputs("BARIS", f); /* timpa 5 byte pertama */
fflush(f);
rewind(f);
char buf[128];
size_t n = fread(buf, 1, sizeof(buf)-1, f);
buf[n] = 0;
printf("r+ isi file: %s", buf);
fclose(f);
}
/* 5) Mode "w+": baca dan tulis dengan truncate */
f = fopen("/tmp/test_modes.txt", "w+");
if (f) {
fputs("konten baru\n", f);
rewind(f);
char buf[64];
if (fgets(buf, sizeof(buf), f)) printf("w+ baca: %s", buf);
fclose(f);
}
/* 6) Mode "a+": append + baca */
f = fopen("/tmp/test_modes.txt", "a+");
if (f) {
fputs("tambahan a+\n", f);
rewind(f);
char buf[128];
size_t n = fread(buf, 1, sizeof(buf)-1, f);
buf[n] = 0;
printf("a+ isi: %s", buf);
fclose(f);
}
return 0;
}Output yang Dihasilkan:
r baca: baris awal
r+ isi file: BARIS awal
baris tambahan
w+ baca: konten baru
a+ isi: konten baru
tambahan a+Perhatikan bahwa setiap mode mengubah keadaan file secara berbeda. Mode "r+" tidak memotong file—kita menimpa 5 byte pertama "baris" menjadi "BARIS", sisanya tetap. Mode "w+" justru memotong file—setelah "w+" dipakai, isi file sebelumnya hilang dan hanya "konten baru\n" yang tersisa. Mode "a+" selalu menulis di akhir file, meskipun kita sempat membaca dengan rewind terlebih dahulu.
Mari kita telaah baris-baris kode di atas:
- Baris 8 (
fopen("/tmp/test_modes.txt", "w");): mode"w"membuat file baru atau memotong file yang sudah ada menjadi kosong. Posisi awal adalah byte ke-0. - Baris 16 (
fopen(..., "r");): mode"r"membuka file yang sudah ada untuk dibaca. Bila file tidak ada,fopenmengembalikanNULL. Tidak pernah membuat file baru. - Baris 22 (
fopen(..., "a");): mode"a"membuka (atau membuat) file untuk menulis di akhir. Setiapfputs/fprintf/fwriteselalu menulis setelah byte terakhir yang ada—file position indicator otomatis lompat ke akhir sebelum setiap penulisan. - Baris 30 (
fputs("BARIS", f);): dengan mode"r+", kita bisa menulis tanpa memotong file. Lima byte pertama"baris"ditimpa dengan"BARIS", sisanya (" awal\n"dan baris tambahan) tetap utuh. - Baris 32 (
fflush(f);): wajib sebelum mengganti arah dari tulis ke baca (atau sebaliknya) pada mode gabungan. Tanpafflush(ataufseek), perilaku undefined. - Baris 39 (
fopen(..., "w+");): mode"w+"memotong file dan memungkinkan baca + tulis. Inilah mode yang sering keliru dipakai ketika programmer maksud"r+". - Baris 50 (
fopen(..., "a+");): mode"a+"memungkinkan baca dari mana saja, tetapi setiap tulis selalu menambah di akhir. Posisi tulis tidak bisa diubah denganfseek—kernel memaksa append.
Binary Mode vs Text Mode #
Mode "b" ("rb", "wb", "ab", dll.) memberitahu C standard library bahwa kita memperlakukan file sebagai kumpulan byte murni tanpa translasi. Pada sistem POSIX (Linux, macOS), text mode dan binary mode identik karena newline POSIX adalah satu byte 0x0A. Pada Windows, text mode menerjemahkan \n menjadi \r\n (CRLF) saat menulis dan sebaliknya saat membaca—mode "b" mematikan translasi ini.
#include <stdio.h>
int main(void) {
/* Tulis 6 byte: termasuk 0x0A (LF) dan 0x0D (CR) */
FILE *f = fopen("/tmp/test_modes.bin", "wb");
if (f) {
unsigned char bytes[] = {0, 1, 2, 0x0A, 0x0D, 255};
fwrite(bytes, 1, sizeof(bytes), f);
fclose(f);
}
/* Baca dengan "rb" — kembalikan apa adanya */
f = fopen("/tmp/test_modes.bin", "rb");
if (f) {
unsigned char b[8];
size_t n = fread(b, 1, sizeof(b), f);
printf("rb %zu byte:", n);
for (size_t i = 0; i < n; i++) printf(" %02X", b[i]);
printf("\n");
fclose(f);
}
return 0;
}Output yang Dihasilkan:
rb 6 byte: 00 01 02 0A 0D FFPada Linux, jumlah byte yang ditulis sama dengan jumlah byte yang dibaca—tidak ada translasi. Jika kita menulis 0x0A (LF), 0x0A yang tersimpan di file. Pada Windows dengan mode "w" (text mode), menulis \n akan menghasilkan dua byte \r\n di file, sehingga 0x0A yang kita kirim bisa berubah menjadi 0x0D 0x0A. Untuk file biner (gambar, executable, data terstruktur), translasi ini merusak data—karena itu selalu gunakan "wb"/"rb" untuk file biner.
Mode “x” C11: Cegah Overwrite #
Sejak C11, kita bisa menambahkan "x" pada mode "w" atau "w+" untuk membuat fopen gagal bila file sudah ada. Berguna untuk file lock, atomic create, atau mencegah bug ganti file konfigurasi secara tak sengaja.
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void) {
/* 1) wx gagal bila file sudah ada */
FILE *f = fopen("/tmp/test_modes.txt", "wx");
if (!f) {
printf("wx gagal (file sudah ada), errno=%d: %s\n",
errno, strerror(errno));
} else {
fputs("tidak akan dieksekusi\n", f);
fclose(f);
}
/* 2) wx sukses untuk file baru */
f = fopen("/tmp/test_modes_unique.txt", "wx");
if (f) {
fputs("file baru dengan wx\n", f);
fclose(f);
printf("wx sukses untuk file baru\n");
} else {
printf("wx gagal: %s\n", strerror(errno));
}
return 0;
}Output yang Dihasilkan:
wx gagal (file sudah ada), errno=17: File exists
wx sukses untuk file baruerrno=17 adalah EEXIST (File exists). Mode "wx" menggunakan system call open(O_CREAT|O_EXCL) di POSIX, yang secara atomik memeriksa dan membuat file—tidak ada celah race condition antara pengecekan dan pembuatan. Inilah dasar pola atomic file creation yang akan kita bahas di chapter atomic-file-updates.