Pengguna adalah sumber input yang tidak bisa dipercaya. Mereka mungkin mengetik huruf ketika diminta angka, menekan Ctrl-D di tengah input, atau memasukkan bilangan yang sangat besar sehingga melampaui batas long. Program yang tidak menangani kasus-kasus ini dengan baik akan crash, menghasilkan nilai absurd, atau berputar tanpa henti. Pada chapter ini kita akan mempelajari tiga lapis pertahanan terhadap input tidak valid: memeriksa nilai kembalian scanf untuk mendeteksi kegagalan, mengganti scanf dengan kombinasi fgets+strtol untuk validasi ketat, serta membangun loop retry yang meminta input ulang dengan batasan jumlah percobaan.
Lapis Pertama: Periksa return value scanf #
Cara paling sederhana menangani input tidak valid adalah memeriksa nilai kembalian scanf. Fungsi ini mengembalikan jumlah item yang berhasil dibaca, atau EOF (biasanya -1) jika input berakhir lebih awal sebelum ada karakter yang bisa dibaca. Jika scanf mencoba membaca %d tetapi input adalah huruf, scanf akan mengembalikan 0 dan meninggalkan karakter yang tidak valid di buffer stdin.
#include <stdio.h>
int main(void) {
int umur;
printf("Masukkan umur Anda: ");
int hasil = scanf("%d", &umur);
if (hasil == EOF) {
printf("Error: input berakhir (EOF).\n");
return 1;
} else if (hasil == 0) {
printf("Error: input bukan angka. Coba lagi.\n");
return 1;
} else {
printf("Umur Anda: %d\n", umur);
}
return 0;
}Output yang Dihasilkan untuk berbagai input:
$ echo "25" | ./program
Masukkan umur Anda: Umur Anda: 25
$ echo "bukan_angka" | ./program
Masukkan umur Anda: Error: input bukan angka. Coba lagi.
$ printf "" | ./program
Masukkan umur Anda: Error: input berakhir (EOF).Mari kita telaah baris-baris kode di atas:
- Baris 6 (
int hasil = scanf("%d", &umur);): nilai kembalianscanfdisimpan dihasil. Jika berhasil membaca satu integer, nilai kembalian adalah1. - Baris 8 (
if (hasil == EOF)): jika pengguna menekan Ctrl-D (atau input pipa berakhir) tanpa mengetik apa pun,scanfmengembalikanEOF(konstanta integer negatif, biasanya-1). - Baris 11 (
else if (hasil == 0)): jika input dimulai dengan karakter yang bukan digit (atau tanda minus),scanfgagal membaca integer dan mengembalikan0. Karakter yang tidak valid tetap berada di buffer stdin. - Baris 14 (
else): kasus sukses. Nilai kembalian1berarti satu item integer berhasil dibaca dan disimpan ke variabelumur.
Lapis Kedua: Validasi Ketat dengan fgets dan strtol #
Pendekatan yang lebih kokoh adalah membaca seluruh baris dengan fgets, lalu memvalidasinya dengan strtol yang melaporkan di mana parsing berhenti. Dengan memeriksa endptr, kita bisa memastikan seluruh string adalah angka valid tanpa karakter sisa seperti 12.5 atau 12abc. Kita juga memeriksa errno untuk mendeteksi overflow.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
/* Cek apakah string berisi bilangan integer valid */
int parse_int(const char *s, long *out) {
/* Lewati whitespace awal */
while (*s != '\0' && isspace((unsigned char)*s)) {
s++;
}
if (*s == '\0') return 0; /* string kosong */
errno = 0;
char *endptr;
long v = strtol(s, &endptr, 10);
if (endptr == s) return 0; /* tidak ada angka */
if (errno == ERANGE) return 0; /* overflow */
while (*endptr != '\0' && isspace((unsigned char)*endptr)) {
endptr++;
}
if (*endptr != '\0') return 0; /* ada karakter sisa */
*out = v;
return 1;
}
int main(void) {
char input[64];
printf("Masukkan bilangan bulat: ");
if (fgets(input, sizeof(input), stdin) == NULL) {
fprintf(stderr, "Error: input berakhir (EOF).\n");
return 1;
}
input[strcspn(input, "\n")] = '\0';
long nilai;
if (parse_int(input, &nilai)) {
printf("Valid: nilai = %ld\n", nilai);
} else {
fprintf(stderr, "Error: \"%s\" bukan bilangan bulat valid.\n", input);
return 1;
}
return 0;
}Output yang Dihasilkan untuk berbagai input:
$ echo "42" | ./program
Masukkan bilangan bulat: Valid: nilai = 42
$ echo "-7" | ./program
Masukkan bilangan bulat: Valid: nilai = -7
$ echo "12.5" | ./program
Masukkan bilangan bulat: Error: "12.5" bukan bilangan bulat valid.
$ echo "abc" | ./program
Masukkan bilangan bulat: Error: "abc" bukan bilangan bulat valid.
$ echo "99999999999999999999" | ./program
Masukkan bilangan bulat: Error: "99999999999999999999" bukan bilangan bulat valid.Mari kita telaah baris-baris kode di atas:
- Baris 11 (
int parse_int(const char *s, long *out)): fungsi helper yang mengembalikan1jika stringsberisi bilangan bulat valid, dan menulis hasilnya ke*out. Jika tidak valid, mengembalikan0tanpa memodifikasi*out. - Baris 13-15 (loop
whilepertama): melewatkan whitespace di awal string agar input seperti" 42"tetap diterima. - Baris 16 (
if (*s == '\0') return 0;): jika setelah whitespace string kosong, berarti input tidak mengandung angka sama sekali. - Baris 18 (
errno = 0;): wajib direset sebelum memanggilstrtolagar kita tidak membaca error lama. - Baris 21 (
long v = strtol(s, &endptr, 10);): mengonversi string menjadilongbasis 10.endptrakan menunjuk ke karakter pertama yang tidak ikut diparse. - Baris 23 (
if (endptr == s) return 0;): jikaendptrsama dengans, tidak ada karakter angka yang berhasil dibaca (misalnya input"abc"). - Baris 24 (
if (errno == ERANGE) return 0;): jika input melebihi bataslong,strtolmengaturerrno = ERANGE. Input seperti99999999999999999999akan tertangkap di sini. - Baris 25-27 (loop
whilekedua): melewatkan whitespace di akhir string agar input seperti"42 "tetap diterima. - Baris 28 (
if (*endptr != '\0') return 0;): jika ada karakter selain whitespace di akhir (misalnya"12.5"atau"12abc"), input tidak valid. - Baris 31 (
return 1;): sukses. Nilaivditulis ke*outdan fungsi mengembalikan1.
Lapis Ketiga: Loop Retry dengan Batas Percobaan #
Setelah kita punya fungsi validasi, kita bisa membangun loop yang meminta input ulang sampai valid atau sampai pengguna kehabisan percobaan. Pola ini umum di program CLI yang tidak ingin crash pada kesalahan pertama, tetapi juga ingin mencegah loop tak terhingga akibat pengguna yang terus memasukkan input salah.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
int parse_int(const char *s, long *out) {
while (*s != '\0' && isspace((unsigned char)*s)) s++;
if (*s == '\0') return 0;
errno = 0;
char *endptr;
long v = strtol(s, &endptr, 10);
if (endptr == s) return 0;
if (errno == ERANGE) return 0;
while (*endptr != '\0' && isspace((unsigned char)*endptr)) endptr++;
if (*endptr != '\0') return 0;
*out = v;
return 1;
}
int main(void) {
char input[64];
long nilai;
int percobaan = 0;
int maks_percobaan = 3;
while (percobaan < maks_percobaan) {
printf("Masukkan umur (0-150): ");
if (fgets(input, sizeof(input), stdin) == NULL) {
fprintf(stderr, "\nError: input berakhir (EOF).\n");
return 1;
}
input[strcspn(input, "\n")] = '\0';
if (!parse_int(input, &nilai)) {
percobaan++;
fprintf(stderr, " Bukan angka. Sisa percobaan: %d\n",
maks_percobaan - percobaan);
continue;
}
if (nilai < 0 || nilai > 150) {
percobaan++;
fprintf(stderr, " Umur di luar rentang 0-150. Sisa percobaan: %d\n",
maks_percobaan - percobaan);
continue;
}
printf("Terima kasih! Umur Anda: %ld\n", nilai);
return 0;
}
fprintf(stderr, "Terlalu banyak percobaan. Program berhenti.\n");
return 1;
}Output yang Dihasilkan (input: abc, 200, 25):
Masukkan umur (0-150): Masukkan umur (0-150): Masukkan umur (0-150): Terima kasih! Umur Anda: 25Output yang Dihasilkan (input: abc, 200, -5):
Masukkan umur (0-150): Masukkan umur (0-150): Masukkan umur (0-150): Terlalu banyak percobaan. Program berhenti.Dengan stderr yang menampilkan:
Bukan angka. Sisa percobaan: 2
Umur di luar rentang 0-150. Sisa percobaan: 1
Umur di luar rentang 0-150. Sisa percobaan: 0Mari kita telaah baris-baris kode di atas:
- Baris 26 (
int maks_percobaan = 3;): konstanta batas percobaan. Pengguna hanya boleh gagal 3 kali sebelum program menyerah. - Baris 28 (
while (percobaan < maks_percobaan)): loop terus berjalan selama jumlah percobaan belum mencapai batas. - Baris 30 (
if (fgets(input, sizeof(input), stdin) == NULL)): jika pengguna menekan Ctrl-D di tengah input,fgetsmengembalikanNULL. Kita laporkan error dan keluar. - Baris 35 (
if (!parse_int(input, &nilai))): jika parsing gagal, tambah penghitung percobaan dan laporkan kestderr. Pernyataancontinuemelompat ke iterasi berikutnya. - Baris 40 (
if (nilai < 0 || nilai > 150)): validasi tambahan — angka valid secara sintaks tetapi di luar rentang yang masuk akal. Input200atau-5akan tertangkap di sini. - Baris 45 (
return 0;): jika semua validasi lolos, cetak konfirmasi sukses dan keluar dengan exit code0. - Baris 49 (
fprintf(stderr, "Terlalu banyak percobaan...")): jika loop keluar karena kehabisan percobaan, laporkan kestderrdan keluar dengan exit code1.