Setiap analisis data dimulai dari satu kemampuan dasar: membaca berkas dengan benar. Pada bagian ini kita membangun versi pertama pengolah CSV — program baris perintah yang membaca laporan penjualan kedai kopi, mengubah setiap baris menjadi data yang bisa dihitung, lalu mencetak tabel omzet yang rapi ke layar. Program ini akan tumbuh menjadi alat analisis lengkap di bagian-bagian berikutnya, jadi kita pastikan fondasinya kokoh sejak sekarang.

Data Latihan #

Buat folder kerja baru untuk proyek ini, lalu simpan teks berikut sebagai sample.csv:

text
tanggal,produk,kategori,jumlah,harga
2025-06-01,Kopi Susu,Minuman,3,18000
2025-06-01,Roti Cokelat,Makanan,2,12000
2025-06-02,Es Teh Manis,Minuman,5,8000
2025-06-02,Kopi Hitam,Minuman,2,15000
2025-06-03,Nasi Goreng,Makanan,1,25000
2025-06-03,Air Mineral,Minuman,4,5000
2025-06-04,Kopi Susu,Minuman,2,18000
2025-06-04,Keripik Kentang,Camilan,3,10000
2025-06-05,Es Teh Manis,Minuman,3,8000
2025-06-05,Roti Keju,Makanan,1,20000
2025-06-06,Kopi Hitam,Minuman,4,15000
2025-06-06,Puding Karamel,Camilan,2,14000

Satu baris merekam satu transaksi: tanggal penjualan, nama produk, kategori, jumlah item yang terjual, dan harga satuan dalam rupiah. Data di atas sengaja bersih — dua belas baris yang seluruhnya terisi lengkap — agar kita bisa fokus pada mekanisme membaca. Bagian 2 nanti mengotori berkas ini dengan kesalahan yang realistis.

Membaca Baris dengan DictReader #

Chapter Membaca Data CSV sudah memperkenalkan alur kerjanya: csv.DictReader memakai baris pertama berkas sebagai nama kolom, lalu menghasilkan satu dictionary per baris data. Kita bungkus alur itu menjadi fungsi baca_data() dalam main.py:

python
import csv


def baca_data(nama_file):
    """Baca seluruh baris file CSV dan kembalikan sebagai list of dict."""
    with open(nama_file, newline="", encoding="utf-8") as berkas:
        return list(csv.DictReader(berkas))

Mari kita telaah peran setiap baris:

  1. Baris 1 (import csv): mengimpor module csv dari standard library. Seluruh proyek ini hanya membutuhkan module ini, sys, dan datetime.
  2. Baris 4 (def baca_data(nama_file):): fungsi menerima nama berkas sebagai string dan mengembalikan list berisi dict — satu elemen per baris data.
  3. Baris 6 (open(nama_file, newline="", ...)): argumen newline="" mencegah Python menerjemahkan karakter baris baru di dalam berkas CSV, sedangkan encoding="utf-8" memastikan nama produk berbahasa Indonesia terbaca benar di sistem operasi mana pun.
  4. Baris 7 (return list(...)): DictReader adalah iterator yang malas; list() memaksa seluruh baris terbaca sekaligus sebelum berkas ditutup oleh with.

Ada satu perilaku DictReader yang harus kita sadari sejak awal. Program kecil berikut membaca baris pertama lalu memeriksa tipe kolom harga:

python
import csv

with open("sample.csv", newline="", encoding="utf-8") as berkas:
    baris_pertama = next(csv.DictReader(berkas))

print(baris_pertama)
print(type(baris_pertama["harga"]))

Output yang Dihasilkan:

text
{'tanggal': '2025-06-01', 'produk': 'Kopi Susu', 'kategori': 'Minuman', 'jumlah': '3', 'harga': '18000'}
<class 'str'>

Module csv tidak mengonversi tipe apa pun. Nilai '18000' adalah string, bukan angka, sehingga operasi int(baris["jumlah"]) * int(baris["harga"]) wajib mengonversi keduanya lebih dulu — kalau tidak, interpreter Python menolak perkalian antar dua string dengan TypeError. Konversi eksplisit seperti ini adalah pekerjaan kita, bukan pekerjaan module csv.

Menghitung Omzet per Baris #

Konversi tipe itu kita pusatkan dalam satu fungsi kecil:

python
def hitung_omzet(baris):
    """Hitung omzet satu baris: jumlah dikali harga."""
    return int(baris["jumlah"]) * int(baris["harga"])

Fungsi hitung_omzet() menerima satu dictionary baris, mengonversi jumlah dan harga ke int, lalu mengembalikan hasil kalinya. Mempusatkan rumus di satu tempat membuat kesalahan konversi mudah dilacak, dan fungsi murni seperti ini akan sangat memudahkan pengujian pada Bagian 4.

Merapikan Keluaran dengan f-string #

Sisa program adalah titik masuk main() yang memeriksa argumen, membaca berkas, lalu mencetak tabel:

python
import sys


def main():
    if len(sys.argv) != 2:
        print("Pemakaian: python3 main.py <file.csv>")
        return 1

    nama_file = sys.argv[1]
    try:
        data = baca_data(nama_file)
    except FileNotFoundError:
        print(f"File tidak ditemukan: {nama_file}")
        return 1

    print(f"{'Tanggal':<12}{'Produk':<16}{'Jumlah':>6}{'Harga':>10}{'Omzet':>12}")
    print("-" * 56)

    total_omzet = 0
    for baris in data:
        omzet = hitung_omzet(baris)
        total_omzet += omzet
        print(
            f"{baris['tanggal']:<12}"
            f"{baris['produk']:<16}"
            f"{baris['jumlah']:>6}"
            f"{int(baris['harga']):>10}"
            f"{omzet:>12,}"
        )

    print("-" * 56)
    print(f"Total baris : {len(data)}")
    print(f"Total omzet : Rp{total_omzet:,}")

    return 0


if __name__ == "__main__":
    sys.exit(main())

Mari kita bedah bagian-bagian barunya:

  1. Baris 5 (if len(sys.argv) != 2:): sys.argv berisi nama skrip beserta argumen baris perintah. Panjang yang diharapkan adalah 2 — nama skrip dan satu nama berkas. Bila pengguna lupa memberi argumen, program mencetak petunjuk pemakaian dan mengembalikan exit code 1.
  2. Baris 10-14 (try/except FileNotFoundError): pembacaan berkas dibungkus try sehingga nama berkas yang salah tidak membuat traceback mentah, melainkan pesan yang jelas plus exit code 1.
  3. Baris 16 (f"{'Tanggal':<12}..."): kepala tabel memakai spesifikasi perataan yang sama dengan baris data, sehingga kolomnya sejajar. <12 berarti rata kiri dengan lebar 12 karakter; >6 berarti rata kanan dengan lebar 6.
  4. Baris 21-28 (omzet = hitung_omzet(baris)): omzet setiap baris dihitung dan diakumulasi ke total_omzet, lalu dicetak dengan {omzet:>12,} pada baris 28 — tanda koma pada spesifikasi menambahkan pemisah ribuan, misalnya 54,000.
  5. Baris 38 (if __name__ == "__main__":): blok ini memastikan main() hanya berjalan saat berkas dieksekusi langsung. Saat main.py diimpor oleh berkas test pada Bagian 4, interpreter Python melewati blok ini.

Spesifikasi format yang dipakai tabel dirangkum berikut:

SpesifikasiEfek
{nilai:<12}rata kiri, lebar minimum 12 karakter
{nilai:>6}rata kanan, lebar minimum 6 karakter
{nilai:>12,}rata kanan, lebar 12, dengan pemisah ribuan

Menjalankan Program #

Program sudah lengkap. Jalankan dari folder yang sama dengan sample.csv:

bash
python3 main.py sample.csv

Output yang Dihasilkan:

text
Tanggal     Produk          Jumlah     Harga       Omzet
--------------------------------------------------------
2025-06-01  Kopi Susu            3     18000      54,000
2025-06-01  Roti Cokelat         2     12000      24,000
2025-06-02  Es Teh Manis         5      8000      40,000
2025-06-02  Kopi Hitam           2     15000      30,000
2025-06-03  Nasi Goreng          1     25000      25,000
2025-06-03  Air Mineral          4      5000      20,000
2025-06-04  Kopi Susu            2     18000      36,000
2025-06-04  Keripik Kentang      3     10000      30,000
2025-06-05  Es Teh Manis         3      8000      24,000
2025-06-05  Roti Keju            1     20000      20,000
2025-06-06  Kopi Hitam           4     15000      60,000
2025-06-06  Puding Karamel       2     14000      28,000
--------------------------------------------------------
Total baris : 12
Total omzet : Rp391,000

Tanpa argumen, program menolak dengan sopan:

bash
python3 main.py

Output yang Dihasilkan:

text
Pemakaian: python3 main.py <file.csv>

Fondasi pembaca sudah berdiri: baca_data() mengubah berkas menjadi struktur data Python, hitung_omzet() mengonversi dan menghitung, dan main() merapikan tampilannya. Kode lengkap tahap ini tersedia pada bagian Berkas Sumber di bawah halaman ini. Data sesuci sample.csv jarang bertahan lama di dunia nyata — pada Bagian 2 kita menghadapkan program ini pada berkas yang mengandung kesalahan.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

main.py

Unduh berkas
python
"""Pengolah File CSV — Bagian 1: Membaca data penjualan."""

import csv
import sys


def baca_data(nama_file):
    """Baca seluruh baris file CSV dan kembalikan sebagai list of dict."""
    with open(nama_file, newline="", encoding="utf-8") as berkas:
        return list(csv.DictReader(berkas))


def hitung_omzet(baris):
    """Hitung omzet satu baris: jumlah dikali harga."""
    return int(baris["jumlah"]) * int(baris["harga"])


def main():
    if len(sys.argv) != 2:
        print("Pemakaian: python3 main.py <file.csv>")
        return 1

    nama_file = sys.argv[1]
    try:
        data = baca_data(nama_file)
    except FileNotFoundError:
        print(f"File tidak ditemukan: {nama_file}")
        return 1

    print(f"{'Tanggal':<12}{'Produk':<16}{'Jumlah':>6}{'Harga':>10}{'Omzet':>12}")
    print("-" * 56)

    total_omzet = 0
    for baris in data:
        omzet = hitung_omzet(baris)
        total_omzet += omzet
        print(
            f"{baris['tanggal']:<12}"
            f"{baris['produk']:<16}"
            f"{baris['jumlah']:>6}"
            f"{int(baris['harga']):>10}"
            f"{omzet:>12,}"
        )

    print("-" * 56)
    print(f"Total baris : {len(data)}")
    print(f"Total omzet : Rp{total_omzet:,}")

    return 0


if __name__ == "__main__":
    sys.exit(main())