Berkas teks menyimpan data sebagai deretan karakter yang masih dapat dibaca manusia, dan stdio.h menyediakan keluarga fungsi yang bekerja pada tingkat karakter, baris, serta data terformat. Topik ini membahas penulisan dengan fputs dan fprintf, pembacaan karakter dengan fgetc, pembacaan baris demi baris dengan fgets, dan pembacaan data terformat dengan fscanf. Setelah membacanya, isi berkas teks dapat ditulis dan dibaca dengan pola yang aman, termasuk mengetahui kapan pembacaan berakhir.
Menulis teks dengan fputs dan fprintf#
Fungsi fputs menulis satu teks ke stream apa adanya, tanpa penambahan pindah baris dan tanpa pemformatan. Bila isi yang ditulis perlu disusun dari beberapa nilai, fungsi fprintf bekerja persis seperti printf. Bedanya, argumen pertama fprintf adalah stream tujuan, sehingga hasilnya mengalir ke berkas alih-alih ke layar. Kedua fungsi mengembalikan nilai yang dapat diperiksa, yaitu bilangan tak negatif saat berhasil untuk fputs dan jumlah karakter tercetak untuk fprintf. Program berikut menyusun berkas rekap.txt dengan pemformatan kolom:
#include <stdio.h>
int main(void) {
FILE *berkas = fopen("rekap.txt", "w");
if (berkas == NULL) {
perror("fopen rekap.txt");
return 1;
}
fprintf(berkas, "Jadwal piket kelas\n");
fprintf(berkas, "------------------\n");
fprintf(berkas, "%-8s %s\n", "Senin", "Ali");
fprintf(berkas, "%-8s %s\n", "Selasa", "Budi");
fprintf(berkas, "%-8s %s\n", "Rabu", "Citra");
fclose(berkas);
printf("rekap.txt selesai ditulis.\n");
return 0;
}rekap.txt selesai ditulis.# kompilasi dan jalankan program penulis
gcc tulis-rekap.c -o tulis-rekap && ./tulis-rekap
# tampilkan isi berkas yang baru ditulis
cat rekap.txtrekap.txt selesai ditulis.
Jadwal piket kelas
------------------
Senin Ali
Selasa Budi
Rabu CitraPenentu %-8s meratakan nama hari ke kiri dalam kolom selebar delapan karakter, sehingga berkasnya tampak rapi ketika dibuka di editor mana pun. Semua penentu format yang berlaku pada printf berlaku pula di sini karena fprintf menggunakan mesin pemformatan yang sama dengan printf. Pindah baris \n dituliskan secara eksplisit pada setiap akhir baris, karena tidak ada fungsi penulis berkas yang menambahkannya secara otomatis.
Bila pemformatan tidak diperlukan, fputs menjadi pilihan yang lebih ringkas karena langsung menerima teks jadi. Nilai kembali kedua fungsi ini tetap layak diperiksa pada kode kritis, misalnya saat berkas yang ditulis tidak boleh berhenti setengah jalan. Disk penuh dapat menghentikan penulisan tanpa pesan apa pun.
Membaca satu karakter dengan fgetc#
Isi berkas belanja.txt berikut menjadi bahan bacaan untuk dua contoh selanjutnya:
Beras 5 kg
Minyak 2 L
Gula 1 kgFungsi fgetc membaca satu karakter berikutnya dari stream dan mengembalikan kodenya bertipe int. Nilai kembali sengaja bukan char agar EOF tidak pernah tertukar dengan kode karakter. EOF adalah penanda habisnya data yang bernilai negatif, sedangkan kode karakter semuanya bernilai nol ke atas. Pola baca yang paling umum adalah perulangan yang kondisinya membandingkan hasil fgetc dengan EOF:
#include <stdio.h>
int main(void) {
FILE *berkas = fopen("belanja.txt", "r");
if (berkas == NULL) {
perror("fopen belanja.txt");
return 1;
}
int c;
int jumlah = 0;
while ((c = fgetc(berkas)) != EOF) {
putchar(c);
jumlah++;
}
fclose(berkas);
printf("Total karakter: %d\n", jumlah);
return 0;
}Beras 5 kg
Minyak 2 L
Gula 1 kg
Total karakter: 32Keluaran program sama persis dengan isi berkasnya, karena setiap karakter termasuk pindah baris ikut terbaca dan dicetak kembali oleh putchar. Total 32 karakter mencakup tiga pindah baris, sehingga penghitungan semacam ini juga berguna untuk statistik ukuran teks. Pembacaan per karakter lambat bagi berkas besar, tetapi pola ini menjadi dasar bagi fungsi pembaca baris berikutnya.
Perhatikan urutan operasi dalam kondisi perulangan tersebut, yaitu pembacaan dilakukan terlebih dahulu, hasilnya disimpan pada c, baru dibandingkan dengan EOF. Urutan ini menjamin setiap karakter terproses tepat satu kali dan perulangan berhenti pada bacaan yang gagal. Pola yang sama berlaku saat pembacaan diarahkan ke keyboard, karena getchar yang dibahas pada modul Dasar Bahasa pada dasarnya adalah fgetc untuk stream masukan standar.
Membaca baris dengan fgets#
Fungsi fgets membaca satu baris ke dalam buffer yang disediakan pemanggil. Argumen keduanya menentukan ukuran buffer, dan fgets tidak pernah menulis melebihi itu karena paling banyak n - 1 karakter yang dibaca, menyisakan satu slot untuk penanda akhir \0. Pembacaan berhenti setelah pindah baris, dan pindah baris itu ikut disimpan di dalam buffer. Nilai kembanya adalah pointer buffer saat berhasil dan NULL saat data sudah habis atau terjadi error:
#include <stdio.h>
int main(void) {
FILE *berkas = fopen("belanja.txt", "r");
if (berkas == NULL) {
perror("fopen belanja.txt");
return 1;
}
char baris[100];
int nomor = 1;
while (fgets(baris, sizeof baris, berkas) != NULL) {
printf("%d. %s", nomor, baris);
nomor++;
}
fclose(berkas);
return 0;
}1. Beras 5 kg
2. Minyak 2 L
3. Gula 1 kgPerhatikan bahwa format "%d. %s" tidak diakhiri \n, karena pindah baris sudah terbawa di dalam buffer. Apabila baris perlu diproses tanpa pindah barisnya, misalnya untuk dibandingkan dengan teks lain, karakter itu dihapus terlebih dahulu. Kombinasi fungsi strcspn dari string.h dengan penanda \0 menjadi idiom yang ringkas untuk keperluan itu:
// tidak-dijalankan
baris[strcspn(baris, "\n")] = '\0';Ekspresi tersebut mencari posisi pertama pindah baris dalam baris, lalu menempatkan penanda akhir tepat di sana. Bila tidak ada pindah baris, misalnya karena barisnya lebih panjang dari buffer, strcspn mengembalikan panjang teks dan penanda akhir ditempatkan di ujungnya. Dengan begitu, idiom ini aman dalam kedua keadaan.
Membaca data terformat dengan fscanf#
Data bernilai seperti daftar nama dan angka lebih praktis dibaca dengan fscanf. Fungsi ini bekerja seperti scanf, tetapi argumen pertamanya berupa stream sumber. Nilai kembanya berupa jumlah data yang berhasil dibaca serta diubah. Justru nilai kembali itulah yang menjadi kondisi perulangan, karena pembacaan berhenti tepat ketika data tidak lagi lengkap. Berkas nilai.txt berikut memuat tiga pasang nama dan angka:
Ali 90
Budi 75
Citra 88#include <stdio.h>
int main(void) {
FILE *berkas = fopen("nilai.txt", "r");
if (berkas == NULL) {
perror("fopen nilai.txt");
return 1;
}
char nama[32];
int nilai = 0;
int jumlah = 0;
int total = 0;
while (fscanf(berkas, "%31s %d", nama, &nilai) == 2) {
printf("%s: %d\n", nama, nilai);
total += nilai;
jumlah++;
}
fclose(berkas);
if (jumlah > 0) {
printf("Rata-rata: %.1f\n", (double)total / jumlah);
}
return 0;
}Ali: 90
Budi: 75
Citra: 88
Rata-rata: 84.3Penentu %31s membaca satu kata paling banyak 31 karakter, menyisakan satu slot untuk \0 pada buffer 32 karakter. Selama pasangan kata dan angka lengkap, nilai kembali fscanf adalah 2 dan perulangan berlanjut. Saat berkas habis, nilainya bukan 2, sehingga perulangan berhenti dengan sendirinya. Perhitungan rata-rata membagi total dengan jumlah data, dan hasilnya ditampilkan dengan satu angka di belakang koma.
Kesalahan umum#
Kesalahan pertama adalah menampung hasil fgetc dalam variabel char. Bila tipe char pada suatu platform tidak mampu menampung nilai negatif, perbandingan dengan EOF tidak pernah benar dan perulangannya tidak pernah berhenti. Deklarasikan penampungnya sebagai int, karena justru tipe itulah yang dikembalikan fgetc.
Kesalahan kedua adalah lupa bahwa fgets menyimpan pindah baris di ujung buffer. Perbandingan strcmp(baris, "Beras 5 kg") menghasilkan nilai tidak sama karena teks yang tersimpan masih bersambung dengan \n, dan panjang baris yang dihitung selalu lebih satu. Hapus pindah barisnya dengan idiom strcspn di atas, atau pertimbangkan keberadaannya dalam perhitungan.
Kesalahan ketiga adalah mengabaikan nilai kembali fscanf dan langsung menggunakan variabel penampungnya. Saat berkas habis atau datanya tidak cocok format, variabel lama tetap terpakai sehingga data terakhir tercetak dua kali tanpa disadari. Kesalahan sejenis berlaku pada fgets dan fgetc juga, karena semua fungsi baca menuntut nilai kembanya diperiksa sebelum hasilnya digunakan. Kondisikan perulangan pada nilai kembali fungsi bacanya, dan periksa habis atau error secara tuntas pada topik Penanganan Error Berkas.
Ringkasan#
- fputs menulis teks apa adanya ke stream, sedangkan fprintf menulis dengan pemformatan lengkap milik printf.
- fgetc membaca satu karakter dan mengembalikan
int, dengan EOF sebagai penanda habisnya data. - fgets membaca satu baris ke buffer berukuran tetap, menyertakan pindah barisnya, dan mengembalikan
NULLsaat berakhir. - fscanf mengubah teks berkas menjadi nilai terformat, dan nilai kembanya menjadi kondisi alami perulangan baca.
Referensi#
- fgetc, getc — cppreference — Dokumentasi fgetc beserta penjelasan EOF dan tipenya.
- fgets, gets_s — cppreference — Dokumentasi fgets, batas buffer, dan nilai kembanya.
- fscanf, scanf, sscanf — cppreference — Dokumentasi keluarga fscanf beserta penentu formatnya.
- fprintf(3) — Linux manual pages — Man page keluarga printf yang mencakup fprintf di Linux.