Input dunia nyata jarang datang dalam bentuk satu nilai tunggal. Sebuah baris log bisa berisi timestamp, level, dan pesan; sebuah baris CSV berisi banyak field yang dipisahkan koma; sebuah query konfigurasi bisa berisi pasangan key=value. Untuk memecah input seperti ini menjadi bagian-bagian yang dapat diolah, C menyediakan beberapa fungsi: sscanf untuk memparse string yang sudah ada di memori, strtok untuk memecah string berdasarkan delimiter, dan strchr untuk mencari karakter tertentu. Pada chapter ini kita akan mempelajari ketiganya dengan contoh konkret yang sering muncul di dunia nyata.
Memparse String dengan sscanf #
Fungsi sscanf bekerja seperti scanf, tetapi sumbernya bukan stream stdin melainkan sebuah string yang sudah ada di memori. Karena sumbernya berupa string yang sudah final, sscanf cocok untuk parsing tahap kedua: kita baca satu baris penuh memakai fgets, lalu kita pisahkan isinya dengan sscanf. Pendekatan dua tahap ini lebih aman daripada scanf langsung karena kita bisa memvalidasi baris secara keseluruhan sebelum memparse.
#include <stdio.h>
int main(void) {
char input[] = "Budi 17 168.5";
char nama[32];
int umur;
float tinggi;
int jml = sscanf(input, "%31s %d %f", nama, &umur, &tinggi);
printf("Terbaca %d item:\n", jml);
printf(" Nama : %s\n", nama);
printf(" Umur : %d\n", umur);
printf(" Tinggi : %.2f\n", tinggi);
return 0;
}Output yang Dihasilkan:
Terbaca 3 item:
Nama : Budi
Umur : 17
Tinggi : 168.50Mari kita telaah baris-baris kode di atas:
- Baris 5 (
char input[] = "Budi 17 168.5";): string sumber yang akan diparse. Berbeda denganscanfyang membaca daristdin,sscanfmembaca dari string yang sudah ada di memori. - Baris 9 (
int jml = sscanf(input, "%31s %d %f", nama, &umur, &tinggi);): tiga penanda konversi membaca string, integer, dan float dariinput. Format specifier sama persis denganscanf—%31smembatasi panjang string,%dmembaca integer,%fmembaca float. - Baris 10 (
printf("Terbaca %d item:\n", jml);): nilai kembalianjmladalah jumlah item yang berhasil dibaca. Untuk input valid, hasilnya3. Jika input kurang lengkap atau tipe tidak cocok, nilai kembalian akan lebih kecil. - Baris 11-13: mencetak ketiga nilai yang berhasil diparse. Output
168.50muncul karena presisi%.2fmembatasi dua digit desimal.
Memecah String dengan strtok #
Untuk input yang berisi banyak field dengan delimiter yang sama (misalnya CSV), strtok lebih fleksibel daripada sscanf. Fungsi ini memecah string menjadi token berdasarkan satu atau beberapa karakter delimiter. Setiap pemanggilan strtok mengembalikan pointer ke token berikutnya, dan ketika tidak ada token lagi, ia mengembalikan NULL.
#include <stdio.h>
#include <string.h>
int main(void) {
char input[] = "Budi,Siti,Andi,Wati";
int posisi = 1;
/* strtok memecah string berdasarkan delimiter */
char *token = strtok(input, ",");
while (token != NULL) {
printf("Nama #%d: %s\n", posisi++, token);
token = strtok(NULL, ",");
}
return 0;
}Output yang Dihasilkan:
Nama #1: Budi
Nama #2: Siti
Nama #3: Andi
Nama #4: WatiMari kita telaah baris-baris kode di atas:
- Baris 5 (
char input[] = "Budi,Siti,Andi,Wati";): string sumber. Harus berupa arraycharyang dapat dimodifikasi, bukan string literal"..."karenastrtokakan menulis karakter null di tempat delimiter. - Baris 9 (
char *token = strtok(input, ",");): pemanggilan pertama menerima string sumberinputdan delimiter",".strtokmengembalikan pointer ke token pertama (Budi) dan mengganti koma pertama dengan null terminator internal. - Baris 11 (
printf("Nama #%d: %s\n", posisi++, token);): mencetak nomor urut dan isi token. Post-incrementposisi++menambah nilai setelah dicetak. - Baris 12 (
token = strtok(NULL, ",");): pemanggilan berikutnya melewatkanNULLsebagai argumen pertama, yang berarti “lanjutkan dari posisi internal terakhir”.strtokmengembalikan token berikutnya atauNULLjika tidak ada lagi. - Baris 10 (
while (token != NULL)): loop berhenti ketikastrtokmengembalikanNULL, yaitu ketika seluruh string sudah dipecah.
Kombinasi: Parsing Tanggal dan Key=Value #
Pola parsing yang sering muncul di file konfigurasi adalah pasangan key=value yang dipisahkan oleh karakter tertentu, atau tanggal dengan separator literal seperti 2024-08-12. Kita bisa menggabungkan sscanf (untuk format dengan separator literal) dan strtok+strchr (untuk pasangan key=value).
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char input[] = "2024-08-12 14:30:00";
int tahun, bulan, hari, jam, menit, detik;
/* sscanf dengan separator literal */
int jml = sscanf(input, "%d-%d-%d %d:%d:%d",
&tahun, &bulan, &hari, &jam, &menit, &detik);
printf("Item terbaca: %d\n", jml);
printf("Tanggal : %02d/%02d/%d\n", hari, bulan, tahun);
printf("Waktu : %02d:%02d:%02d\n", jam, menit, detik);
/* Parsing key=value dengan strtok dan strchr */
char config[] = "host=localhost;port=8080;user=budi";
char *kv = strtok(config, ";");
while (kv != NULL) {
char *eq = strchr(kv, '=');
if (eq != NULL) {
*eq = '\0'; /* Pisahkan key dari value */
printf("[%s] = [%s]\n", kv, eq + 1);
}
kv = strtok(NULL, ";");
}
return 0;
}Output yang Dihasilkan:
Item terbaca: 6
Tanggal : 12/08/2024
Waktu : 14:30:00
[host] = [localhost]
[port] = [8080]
[user] = [budi]Mari kita telaah baris-baris kode di atas:
- Baris 10 (
sscanf(input, "%d-%d-%d %d:%d:%d", ...)): format specifier memakai separator literal-, spasi, dan:.sscanfmencocokkan karakter literal di format string dengan karakter di input. Jika separator tidak cocok (misalnya input memakai/bukan-), parsing berhenti di titik tersebut. - Baris 13 (
printf("Tanggal : %02d/%02d/%d\n", ...)): setelah diparse menjadi integer terpisah, kita bisa mencetak ulang dengan format berbeda. Inilah kekuatan parsing: kita memisahkan data dari representasi. - Baris 17 (
char config[] = "host=localhost;port=8080;user=budi";): arraycharyang dapat dimodifikasi — wajib karenastrtokakan menulis null terminator. - Baris 18 (
char *kv = strtok(config, ";");): pemanggilan pertamastrtokmemecahconfigberdasarkan;. Token pertama adalah"host=localhost". - Baris 20 (
char *eq = strchr(kv, '=');):strchrmencari karakter=di dalam token dan mengembalikan pointer ke posisi tersebut, atauNULLjika tidak ditemukan. - Baris 22 (
*eq = '\0';): menimpa=dengan null terminator, sehinggakvsekarang hanya berisi"host"daneq + 1menunjuk ke"localhost". - Baris 23 (
printf("[%s] = [%s]\n", kv, eq + 1);): mencetak key dan value yang sudah dipisahkan.kvberhenti di null terminator baru,eq + 1melompat satu posisi melewati bekas=.