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.

c
#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:

text
Terbaca 3 item:
  Nama   : Budi
  Umur   : 17
  Tinggi : 168.50

Mari kita telaah baris-baris kode di atas:

  1. Baris 5 (char input[] = "Budi 17 168.5";): string sumber yang akan diparse. Berbeda dengan scanf yang membaca dari stdin, sscanf membaca dari string yang sudah ada di memori.
  2. Baris 9 (int jml = sscanf(input, "%31s %d %f", nama, &umur, &tinggi);): tiga penanda konversi membaca string, integer, dan float dari input. Format specifier sama persis dengan scanf — %31s membatasi panjang string, %d membaca integer, %f membaca float.
  3. Baris 10 (printf("Terbaca %d item:\n", jml);): nilai kembalian jml adalah jumlah item yang berhasil dibaca. Untuk input valid, hasilnya 3. Jika input kurang lengkap atau tipe tidak cocok, nilai kembalian akan lebih kecil.
  4. Baris 11-13: mencetak ketiga nilai yang berhasil diparse. Output 168.50 muncul karena presisi %.2f membatasi 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.

c
#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:

text
Nama #1: Budi
Nama #2: Siti
Nama #3: Andi
Nama #4: Wati

Mari kita telaah baris-baris kode di atas:

  1. Baris 5 (char input[] = "Budi,Siti,Andi,Wati";): string sumber. Harus berupa array char yang dapat dimodifikasi, bukan string literal "..." karena strtok akan menulis karakter null di tempat delimiter.
  2. Baris 9 (char *token = strtok(input, ",");): pemanggilan pertama menerima string sumber input dan delimiter ",". strtok mengembalikan pointer ke token pertama (Budi) dan mengganti koma pertama dengan null terminator internal.
  3. Baris 11 (printf("Nama #%d: %s\n", posisi++, token);): mencetak nomor urut dan isi token. Post-increment posisi++ menambah nilai setelah dicetak.
  4. Baris 12 (token = strtok(NULL, ",");): pemanggilan berikutnya melewatkan NULL sebagai argumen pertama, yang berarti “lanjutkan dari posisi internal terakhir”. strtok mengembalikan token berikutnya atau NULL jika tidak ada lagi.
  5. Baris 10 (while (token != NULL)): loop berhenti ketika strtok mengembalikan NULL, 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).

c
#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:

text
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:

  1. Baris 10 (sscanf(input, "%d-%d-%d %d:%d:%d", ...)): format specifier memakai separator literal -, spasi, dan :. sscanf mencocokkan karakter literal di format string dengan karakter di input. Jika separator tidak cocok (misalnya input memakai / bukan -), parsing berhenti di titik tersebut.
  2. 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.
  3. Baris 17 (char config[] = "host=localhost;port=8080;user=budi";): array char yang dapat dimodifikasi — wajib karena strtok akan menulis null terminator.
  4. Baris 18 (char *kv = strtok(config, ";");): pemanggilan pertama strtok memecah config berdasarkan ;. Token pertama adalah "host=localhost".
  5. Baris 20 (char *eq = strchr(kv, '=');): strchr mencari karakter = di dalam token dan mengembalikan pointer ke posisi tersebut, atau NULL jika tidak ditemukan.
  6. Baris 22 (*eq = '\0';): menimpa = dengan null terminator, sehingga kv sekarang hanya berisi "host" dan eq + 1 menunjuk ke "localhost".
  7. Baris 23 (printf("[%s] = [%s]\n", kv, eq + 1);): mencetak key dan value yang sudah dipisahkan. kv berhenti di null terminator baru, eq + 1 melompat satu posisi melewati bekas =.