Kalau kamu pernah menulis program C yang membaca nama lengkap lalu hasilnya terpotong di spasi pertama, kemungkinan besar kamu memakai scanf. Fungsi ini paling cepat dipelajari, tetapi juga paling sering bikin bingung pemula. Perbandingan scanf dan fgets untuk membaca input menunjukkan di mana masing-masing cocok dipakai dan di mana keduanya menyulitkan.
Cara kerja scanf saat membaca input
scanf membaca input sesuai format yang kamu tentukan, lalu berhenti di karakter yang tidak cocok dengan format tersebut. Untuk %s, fungsi ini berhenti di spasi, tab, atau newline. Kalimat “Budi Santoso” hanya tersimpan sebagai “Budi” kalau kamu memakai %s.
#include <stdio.h>
int main(void) {
char nama[50];
printf("Nama lengkap: ");
scanf("%s", nama);
printf("Halo, %sn", nama);
return 0;
}
scanf juga tidak membatasi jumlah karakter yang ditulis ke buffer. Kalau kamu mendeklarasikan char nama[10] lalu pengguna mengetik 30 karakter, sisa karakter itu menimpa memori di sekitarnya. Buffer overflow seperti ini sering muncul di soal latihan dan jadi salah satu penyebab program crash yang sulit dilacak.
Ada trik untuk membatasi lebar input, misalnya scanf("%9s", nama) untuk array berukuran 10. Angka 9 berarti sembilan karakter plus satu untuk penutup string. Trik ini bekerja, tapi kamu harus ingat menyesuaikan angkanya setiap kali ukuran array berubah.
Cara kerja fgets dan kenapa lebih aman
fgets membaca satu baris sampai newline atau sampai batas ukuran yang kamu berikan, lalu menyimpan newline tersebut ke dalam buffer. Kamu wajib menyebutkan ukuran buffer, jadi tidak ada risiko luapan selama angka yang kamu tulis benar.
#include <stdio.h>
#include <string.h>
int main(void) {
char nama[50];
printf("Nama lengkap: ");
fgets(nama, sizeof(nama), stdin);
nama[strcspn(nama, "n")] = ' ';
printf("Halo, %sn", nama);
return 0;
}
Baris strcspn di atas menghapus newline di akhir input supaya hasil cetak tidak melompat ke baris baru. Kalau kamu sering mengolah teks, fungsi-fungsi lain di library string.h akan sering kamu pakai bersama fgets.
Konsekuensinya, fgets selalu membaca sebagai teks. Kalau kamu butuh angka, hasilnya harus dikonversi dulu memakai strtol atau sscanf. Langkah tambahan ini membuat fgets terasa lebih ribet di awal, tetapi jauh lebih terkendali saat program mulai berkembang.
Perbandingan langsung scanf dan fgets
Tabel berikut merangkum perbedaan yang paling terasa saat kamu menulis kode sehari-hari.
| Aspek | scanf | fgets |
|---|---|---|
| Membaca spasi | Tidak, berhenti di spasi untuk %s | Ya, satu baris penuh |
| Batas ukuran buffer | Tidak otomatis, harus ditulis manual | Ditentukan di parameter kedua |
| Newline di akhir | Ditinggalkan di buffer input | Ikut tersimpan di buffer |
| Cocok untuk angka | Langsung, dengan %d atau %f | Perlu konversi tambahan |
| Penanganan input salah | Sulit, karakter sisa mengendap | Lebih mudah dikendalikan |
Newline yang tertinggal di buffer input menjadi sumber bug klasik. Kamu memanggil scanf untuk membaca angka, menekan Enter, lalu memanggil fgets untuk membaca nama. fgets langsung menganggap Enter tadi sebagai isi baris dan melewati pertanyaan nama. Gejala seperti ini biasanya baru ketahuan saat kamu melakukan debugging dan memeriksa isi variabel satu per satu.
Kapan sebaiknya memakai scanf
scanf masih berguna untuk input terstruktur yang formatnya sudah pasti, misalnya membaca dua angka dalam satu baris seperti “10 20” pada soal kompetisi pemrograman. Format "%d %d" langsung memisahkan keduanya tanpa kode tambahan. Untuk pengolahan data per baris, pendekatan seperti ini juga umum ditemui saat kamu menata label dan placeholder di formulir, seperti dibahas dalam tips menata label dan placeholder formulir.
Kalau kamu memilih scanf untuk input teks, selalu tulis batas lebarnya. Kebiasaan ini terlihat sepele, tetapi menyelamatkan banyak program dari crash yang tidak jelas penyebabnya.
Kapan fgets lebih tepat
Pakai fgets untuk semua input yang bisa mengandung spasi: nama, alamat, judul, baris perintah, atau isi file. fgets juga pilihan yang lebih aman saat kamu tidak bisa memastikan panjang input dari pengguna. Program yang menerima input dari file atau pipe sebaiknya memakai fgets karena satu baris bisa sangat panjang.
Kalau programmu butuh membaca beberapa baris sekaligus lalu mengolahnya, fgets di dalam loop akan lebih mudah diatur. Pola ini sering muncul saat kamu mengolah data teks atau membaca konfigurasi sebelum menyusun Makefile untuk proyek C. Untuk kebutuhan web, pilihan arsitektur aplikasi juga perlu dipertimbangkan, misalnya lewat perbandingan Rails API dan full-stack.
Kombinasi keduanya di program yang sama
Banyak program memakai keduanya: scanf untuk menu angka, fgets untuk data teks. Kuncinya adalah membersihkan buffer input setelah scanf. Salah satu caranya membaca sisa karakter sampai newline sebelum memanggil fgets.
int pilihan;
scanf("%d", &pilihan);
while (getchar() != 'n');
char nama[50];
fgets(nama, sizeof(nama), stdin);
Alternatif yang lebih konsisten adalah membaca semua input dengan fgets lalu mengubah teksnya menjadi angka memakai strtol. Pola ini menghilangkan seluruh masalah buffer yang tertinggal, dengan harga satu langkah konversi per input.
Ringkasan
scanf cocok untuk input angka berformat ketat dan cepat ditulis, tetapi rapuh saat berhadapan dengan spasi atau panjang input yang tidak diketahui. fgets menuntut sedikit kode tambahan untuk menghapus newline dan mengubah teks menjadi angka, tetapi jauh lebih aman dan mudah diprediksi. Pemula yang sedang membangun kebiasaan menulis kode lebih diuntungkan memakai fgets sejak awal.
Pertanyaan yang Sering Diajukan
Kenapa scanf hanya membaca satu kata dari nama lengkap?
Karena format %s berhenti membaca saat menemukan spasi. Kalau butuh seluruh baris, gunakan fgets atau format %[^n] dengan batas lebar yang jelas.
Apakah fgets selalu lebih aman daripada scanf?
Untuk membaca teks, ya, karena ukuran buffer wajib disebutkan. Namun hasil fgets tetap perlu diperiksa. Kalau input lebih panjang dari buffer, sisa karakternya masih ada di buffer input dan harus dibaca pada pemanggilan berikutnya.
Bagaimana cara mengubah hasil fgets menjadi angka?
Gunakan strtol untuk bilangan bulat atau strtod untuk bilangan desimal. Keduanya memberi informasi sampai karakter mana konversi berhasil, sehingga kamu bisa menolak input yang tidak valid.