CSV adalah format pertukaran data tabular yang paling umum, tetapi format yang sama ini juga menjadi sumber kerusakan data yang paling sering dijumpai di pipeline. Topik ini membedah anatomi CSV yang penuh jebakan, yaitu pembatas yang tidak seragam, aturan kutipan, dan encoding yang berbeda-beda. Pembahasan dilanjutkan dengan pola kegagalan yang muncul saat berkas CSV diproses. Sebuah berkas CSV yang sengaja dibuat bermasalah beserta skrip pembacanya dipakai sebagai contoh, sehingga setiap jebakan terlihat dalam bentuk error yang nyata.
Pembatas yang tidak seragam#
Nama CSV adalah singkatan dari comma-separated values, yakni data tabular yang nilainya dipisahkan tanda koma. Dalam praktiknya, tidak ada aturan yang mewajibkan koma sebagai satu-satunya pembatas (delimiter), yakni karakter yang memisahkan dua nilai pada satu baris. Aplikasi pengolah angka di wilayah yang memakai koma sebagai pemisah desimal lazim menyimpan CSV dengan titik koma, dan banyak sistem ekspor mengikuti kebiasaan masing-masing. Program yang membaca berkas semacam itu dengan pembatas koma akan menggabungkan beberapa kolom menjadi satu, lalu menggeser seluruh kolom di sebelahnya. Karena itulah parameter pembatas harus dinyatakan secara eksplisit pada setiap langkah pipeline, bukan diserahkan pada nilai bawaan pustaka.
Berkas berikut memuat masalah yang sering ditemukan sekaligus, yaitu pembatas titik koma, nilai yang mengandung koma, dan kutipan ganda.
id;cabang;catatan
1;Gudang Pusat;pengiriman datang pukul 08:00, kondisi aman
2;Cabang Timur;"mesin pengemas rusak, perlu servis, stok tertunda"
3;Cabang Barat;stok normal
4;Cabang Selatan;"catatan: ""kiriman ulang"", stok menyusul besok"Empat baris data di atas memakai titik koma sebagai pembatas. Baris pertama data memuat koma di dalam nilai tanpa kutipan, dan hal itu sah selama pembatasnya titik koma, tetapi berubah menjadi dua bidang begitu berkasnya dibaca ulang dengan pembatas koma. Baris kedua data membungkus nilainya dengan tanda kutip ganda karena nilai tersebut memuat koma, sehingga pembaca yang benar mengeluarkan satu nilai utuh. Baris keempat memuat tanda kutip di dalam nilai, dan polanya diuraikan pada bagian berikutnya.
Kutipan dan escaping#
Nilai yang mengandung pembatas, tanda kutip ganda, atau newline wajib dibungkus tanda kutip ganda menurut kebiasaan yang dirumuskan dalam RFC 4180. Newline adalah karakter pergantian baris. Tanda kutip ganda yang berada di dalam nilai digandakan agar tidak salah dibaca sebagai penutup bidang, dan teknik ini dikenal sebagai escaping, yakni penandaan karakter agar tidak diartikan sebagai bagian sintaks format. Kombinasi pembatas, kutipan, dan karakter pergantian baris inilah yang disebut dialect dalam modul csv, dan setiap pipeline sebaiknya menetapkan dialectnya secara eksplisit.
Baris keempat catatan.csv menunjukkan pola tersebut, yaitu nilai yang memuat kutipan dan koma sekaligus. Pembaca yang benar mengembalikan nilai aslinya utuh, sedangkan parser buatan sendiri yang memenggal baris pada setiap tanda koma menghasilkan nilai yang terpotong. Modul csv bawaan Python mengimplementasikan aturan kutipan ini sebagai perilaku bawaannya, baik saat membaca maupun saat menulis, sehingga parser tidak perlu ditulis ulang. Pustaka tersebut otomatis menambahkan kutipan pada nilai yang memerlukannya saat berkas ditulis.
Encoding yang tidak selalu UTF-8#
Encoding adalah pemetaan antara byte dan karakter, dan satu berkas CSV tidak membawa penanda encoding di dalamnya. UTF-8 menjadi pilihan bawaan hampir semua alat modern, tetapi ekspor dari sistem lama sering ditulis dengan latin-1 atau cp1252 yang memetakan byte secara berbeda untuk karakter di luar ASCII. Selisih pemetaan inilah yang membuat berkas yang tampak biasa saja mendadak gagal dibaca di tengah pipeline.
Kegagalannya berbentuk dua wajah. Pembaca UTF-8 yang menemukan byte yang tidak sah akan menghentikan proses dengan UnicodeDecodeError. Sebaliknya, pembaca latin-1 yang menerima berkas UTF-8 hampir tidak pernah gagal, tetapi menghasilkan karakter rusak yang dikenal sebagai mojibake, misalnya huruf é yang berubah menjadi é. Karena berkasnya tidak membawa petunjuk, encoding yang benar hanya bisa diketahui dari sumbernya, misalnya dokumentasi sistem ekspornya. Byte mentahnya dapat diperiksa untuk mengonfirmasi dugaan tersebut, dan skrip pada bagian berikut menunjukkan caranya.
Membaca berkas bermasalah dengan modul csv#
Modul csv menyelesaikan ketiga jebakan tadi dengan pola yang jelas, yaitu pembatas eksplisit, penanganan kutipan bawaan, dan penangkapan error encoding. Skrip berikut mempraktikkan ketiganya pada berkas yang sudah dibuat di atas.
import csv
# 1. membaca catatan.csv dengan pembatas titik koma
with open("catatan.csv", encoding="utf-8", newline="") as f:
pembaca = csv.reader(f, delimiter=";")
for baris in pembaca:
print(baris)
# 2. menyimulasikan ekspor sistem lain yang ditulis dengan latin-1
with open("suhu-ekspor.csv", "w", encoding="latin-1") as f:
f.write("suhu_c\n")
f.write("29,5\n")
f.write("Suhu maksimum 31°C\n")
with open("suhu-ekspor.csv", "rb") as f:
print(f.read())
# 3. membaca berkas itu sebagai UTF-8 dan menangkap errornya
try:
with open("suhu-ekspor.csv", encoding="utf-8") as f:
print(f.read())
except UnicodeDecodeError as e:
print(f"UnicodeDecodeError pada posisi byte {e.start}: {e.reason}")
# 4. membaca ulang dengan encoding yang benar
with open("suhu-ekspor.csv", encoding="latin-1") as f:
print(f.read())['id', 'cabang', 'catatan']
['1', 'Gudang Pusat', 'pengiriman datang pukul 08:00, kondisi aman']
['2', 'Cabang Timur', 'mesin pengemas rusak, perlu servis, stok tertunda']
['3', 'Cabang Barat', 'stok normal']
['4', 'Cabang Selatan', 'catatan: "kiriman ulang", stok menyusul besok']
b'suhu_c\n29,5\nSuhu maksimum 31\xb0C\n'
UnicodeDecodeError pada posisi byte 28: invalid start byte
suhu_c
29,5
Suhu maksimum 31°CLangkah pertama membaca catatan.csv dengan delimiter=";" dan mengembalikan setiap baris sebagai list nilai yang sudah bebas kutipan. Langkah kedua menyimulasikan ekspor dari sistem lain yang ditulis dengan latin-1, lalu menampilkan byte mentahnya, yang memperlihatkan 0xb0 sebagai lambang derajat versi latin-1. Langkah ketiga mencoba membaca berkas itu sebagai UTF-8, sehingga UnicodeDecodeError tertangkap dan posisi byte yang bermasalah tercetak, bukan menggagalkan seluruh program. Langkah keempat membaca ulang berkas yang sama dengan encoding yang benar dan hasilnya tampil utuh.
Pola seperti ini adalah pertahanan pertama di pipeline, yaitu membaca dengan asumsi yang dinyatakan eksplisit dan menangkap error sedini mungkin. Berkas yang gagal dibaca lebih baik ditolak di awal dengan pesan yang jelas daripada dibiarkan merusak tabel di hilir. Posisi byte yang tercetak juga mempercepat diagnosis karena langsung menunjuk karakter yang menjadi penyebabnya.
Kesalahan umum#
Kesalahan berikut muncul berulang kali saat berkas CSV diproses di pipeline. Ketiganya berbiaya kecil untuk dicegah apabila asumsi pembacaan dinyatakan sejak awal.
- Membaca dengan pembatas koma padahal berkasnya memakai titik koma. Satu kolom memuat seluruh baris dan kolom lain bergeser. Perbaikannya adalah menyatakan parameter pembatas secara eksplisit pada setiap pembacaan.
- Memenggal baris dengan
split(",")untuk menggantikan parser. Nilai yang mengandung koma atau kutipan menjadi terpotong. Perbaikannya adalah menggunakan modul csv agar aturan kutipan ditangani pustaka. - Membuka berkas tanpa parameter encoding dan mengandalkan UTF-8. Ekspor latin-1 memicu UnicodeDecodeError di tengah proses. Perbaikannya adalah mengonfirmasi encoding dari sistem sumber dan memeriksa byte mentahnya untuk memastikannya.
Ringkasan#
- CSV tidak membawa spesifikasi pembatas, kutipan, dan encoding di dalam berkasnya, sehingga ketiganya harus dinyatakan sebagai kontrak eksplisit di setiap langkah pipeline.
- Modul csv menangani pembatas kustom dan kutipan ganda sebagai perilaku bawaannya, sedangkan pemenggalan manual dengan
splitmerusak nilai yang memuat koma. - UnicodeDecodeError menandakan ketidakcocokan encoding, dan byte mentah berkasnya adalah bukti untuk menentukan encoding yang benar.
Referensi#
- Dokumentasi modul csv — Python Software Foundation — Deskripsi resmi pembacaan dan penulisan CSV di Python, termasuk konsep dialect dan parameter delimiter.
- RFC 4180 — IETF — Spesifikasi format CSV yang merumuskan aturan kutipan ganda dan pemisahan baris.