scanf memang praktis untuk membaca token-token pendek, tetapi fungsi tersebut berhenti pada whitespace pertama dan sering meninggalkan karakter tersisa di buffer stdin yang membingungkan panggilan input berikutnya. Untuk membaca kalimat penuh atau nama lengkap dengan spasi, fungsi fgets adalah pilihan yang lebih aman dan lebih prediktif. Pada chapter ini kita akan mempelajari cara kerja fgets, bagaimana fungsi ini mencegah buffer overflow dengan membatasi pembacaan sesuai ukuran array, serta bagaimana kita menghapus karakter newline \n yang sering ikut terbawa di akhir string hasil bacaan.
Cara Kerja fgets #
Fungsi fgets membaca satu baris dari stream yang diberikan (umumnya stdin) dan menyimpannya ke dalam array char yang sudah disiapkan. Fungsi ini menerima tiga argumen: pointer ke buffer tujuan, ukuran maksimum buffer, dan stream sumber. fgets akan berhenti membaca ketika bertemu newline, ketika buffer hampir penuh, atau ketika mencapai end-of-file. Karakter newline yang ditangkap akan ikut disimpan di dalam buffer sebelum null terminator.
#include <stdio.h>
#include <string.h>
int main(void) {
char nama[32];
printf("Masukkan nama lengkap: ");
if (fgets(nama, sizeof(nama), stdin) != NULL) {
/* Hapus newline di akhir jika ada */
nama[strcspn(nama, "\n")] = '\0';
printf("Halo, %s!\n", nama);
}
return 0;
}Output yang Dihasilkan (input: Budi Santoso):
Masukkan nama lengkap: Halo, Budi Santoso!Mari kita telaah baris-baris kode di atas:
- Baris 2 (
#include <string.h>): header yang menyediakan fungsistrcspnuntuk mencari posisi karakter tertentu di dalam string. - Baris 5 (
char nama[32];): arraycharberkapasitas 32 byte untuk menampung input pengguna. - Baris 7 (
if (fgets(nama, sizeof(nama), stdin) != NULL)):fgetsmenerima arraynama, ukuransizeof(nama)(yaitu 32), dan streamstdin. Argumen ukuran ditulis memakaisizeofagar jika ukuran array berubah, batasan ikut menyesuaikan otomatis. Pemeriksaan!= NULLmemastikanfgetstidak gagal (misalnya saat EOF). - Baris 9 (
nama[strcspn(nama, "\n")] = '\0';):strcspn(nama, "\n")mengembalikan indeks karakter newline pertama di dalamnama, atau panjang string jika newline tidak ditemukan. Kita menimpa posisi tersebut dengan null terminator untuk menghapus newline. - Baris 10 (
printf("Halo, %s!\n", nama);): mencetak hasil tanpa karakter newline yang ikut terbawa.
Keamanan Buffer Overflow #
Ketika input pengguna lebih panjang dari kapasitas buffer, fgets tidak pernah menulis melebihi ukuran yang ditentukan. Input yang berlebih akan tetap tersisa di stream stdin dan bisa dibaca oleh panggilan fgets berikutnya. Perilaku ini berbeda dengan gets (yang sudah dihapus dari C11 karena berbahaya) yang menulis tanpa batasan dan rentan serangan buffer overflow.
#include <stdio.h>
#include <string.h>
int main(void) {
char buffer[8];
/* fgets tidak pernah overflow: input panjang dipotong */
printf("Masukkan teks: ");
char *r = fgets(buffer, sizeof(buffer), stdin);
printf("Hasil : \"%s\"\n", buffer);
printf("Return value: %s\n", r == buffer ? "sama dengan buffer" : "NULL");
printf("Panjang : %zu karakter\n", strlen(buffer));
/* Sisa input masih ada di buffer stdin */
char sisa[64];
if (fgets(sisa, sizeof(sisa), stdin) != NULL) {
printf("Sisa input : \"%s", sisa);
}
return 0;
}Output yang Dihasilkan (input: Belajar bahasa C):
Masukkan teks: Hasil : "Belajar"
Return value: sama dengan buffer
Panjang : 7 karakter
Sisa input : " bahasa CMari kita telaah baris-baris kode di atas:
- Baris 5 (
char buffer[8];): arraycharberkapasitas 8 byte. Maksimum 7 karakter bisa disimpan, ditambah 1 byte untuk null terminator. - Baris 9 (
char *r = fgets(buffer, sizeof(buffer), stdin);):fgetsdipanggil dengan batassizeof(buffer)yaitu 8. Return value disimpan di pointerragar bisa diperiksa. - Baris 10 (
printf("Hasil : \"%s\"\n", buffer);): hanya 7 karakter pertama dari input (Belajar) yang masuk ke buffer. Karakter kedelapan diisi null terminator olehfgets. - Baris 11 (
printf("Return value: %s\n", r == buffer ? "..." : "NULL");): jikafgetssukses, nilai kembalian berupa pointer ke buffer yang sama. Jika gagal (misalnya EOF tanpa data), kembalian berupaNULL. - Baris 12 (
printf("Panjang : %zu karakter\n", strlen(buffer));):strlenmenghitung jumlah karakter sebelum null terminator, yaitu7. - Baris 15-17 (
fgets(sisa, sizeof(sisa), stdin)): sisa input" bahasa C\n"masih ada di streamstdindan bisa dibaca olehfgetsberikutnya. Karena tidak ada newline di akhir string"Sisa input : ", output" bahasa Cmuncul tanpa baris baru.
Membaca Banyak Baris #
fgets sangat cocok untuk membaca seluruh isi berkas atau beberapa baris input berturut-turut. Pola umumnya adalah loop while yang terus memanggil fgets sampai return value-nya NULL, yang menandakan akhir input.
#include <stdio.h>
#include <string.h>
int main(void) {
char baris[64];
/* Membaca beberapa baris sampai EOF */
int nomor = 1;
while (fgets(baris, sizeof(baris), stdin) != NULL) {
/* Cek apakah baris terakhir tidak punya newline (input pendek) */
baris[strcspn(baris, "\n")] = '\0';
printf("Baris %d: %s\n", nomor++, baris);
}
printf("Selesai. Total %d baris terbaca.\n", nomor - 1);
return 0;
}Output yang Dihasilkan (input tiga baris: Belajar C, Sangat menyenangkan, Sampai jumpa):
Baris 1: Belajar C
Baris 2: Sangat menyenangkan
Baris 3: Sampai jumpa
Selesai. Total 3 baris terbaca.Mari kita telaah baris-baris kode di atas:
- Baris 7 (
int nomor = 1;): penghitung nomor baris dimulai dari 1 untuk tampilan yang ramah pengguna. - Baris 8 (
while (fgets(baris, sizeof(baris), stdin) != NULL)): setiap iterasi membaca satu baris daristdin. Loop berhenti ketikafgetsmengembalikanNULL, yaitu ketikastdinmencapai EOF (end-of-file). - Baris 10 (
baris[strcspn(baris, "\n")] = '\0';): menghapus newline dari setiap baris yang dibaca. Idiom yang sama seperti sebelumnya — aman untuk baris tanpa newline karenastrcspnmengembalikan panjang string dalam kasus tersebut. - Baris 11 (
printf("Baris %d: %s\n", nomor++, baris);): mencetak nomor baris dan isinya, lalu incrementnomorsetelah dicetak (post-increment). - Baris 13 (
printf("Selesai. Total %d baris terbaca.\n", nomor - 1);):nomorsudah di-increment sekali setelah baris terakhir, jadi total baris yang terbaca adalahnomor - 1.