Berkas teks yang kita tulis di satu komputer tidak selalu terbaca mulus di komputer lain. Dua sumber masalahnya: encoding — kesepakatan bagaimana karakter diterjemahkan menjadi byte — dan newline — cara sistem operasi menandai akhir baris. Chapter Unicode dan Encoding sudah membahas sisi string (str vs bytes, encode()/decode()); chapter ini menerapkannya pada dunia berkas: argument encoding dan newline pada open().
Encoding Tergantung Sistem Operasi #
Ketika open() dipanggil tanpa encoding, Python memakai encoding default sistem operasi, yang bisa ditanyakan lewat module locale:
import locale
print(locale.getpreferredencoding(False))Output yang Dihasilkan:
UTF-8Mesin pengujian materi ini memakai UTF-8, tetapi hasil di komputer lain bisa berbeda — Windows berbahasa Inggris umumnya memakai cp1252, dan Windows di beberapa regional lain memakai cp850 atau sejenisnya. Artinya, program yang “jalan mulus” di laptop kita bisa memicu UnicodeDecodeError di komputer pelanggan hanya karena encoding default-nya berbeda. Karena itu, aturan praktisnya satu kalimat: selalu tulis encoding="utf-8" secara eksplisit pada setiap open() untuk berkas teks, baik saat menulis maupun membaca.
UnicodeDecodeError dari Berkas Nyata #
Mari kita lihat betapa nyatanya masalah ini. Berkas berikut sengaja ditulis dengan encoding Latin-1, yang lazim dipakai aplikasi Eropa lama:
with open("menu.txt", "w", encoding="latin-1") as berkas:
berkas.write("Café Latte - Rp28000\nEs Teh Manis - Rp8000\n")
with open("menu.txt", "rb") as berkas:
data = berkas.read()
print(data)Output yang Dihasilkan:
b'Caf\xe9 Latte - Rp28000\nEs Teh Manis - Rp8000\n'Huruf é tersimpan sebagai satu byte 0xE9 (ditampilkan Python sebagai \xe9) karena begitulah aturan Latin-1. Byte tersebut justru tidak sah sebagai awal karakter UTF-8, sehingga membaca berkas ini sebagai UTF-8 langsung gagal:
>>> with open("menu.txt", encoding="utf-8") as berkas:
... berkas.read()
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<frozen codecs>", line 322, in decode
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 3: invalid continuation bytenewline: \r\n vs \n #
Cara kedua berkas teks saling bertengkar: penanda akhir baris. Linux dan macOS modern memakai \n, sedangkan Windows memakai dua karakter \r\n. Mode teks Python menerjemahkan semua varian tersebut menjadi \n saat membaca — perilaku ini disebut universal newlines:
with open("windows.txt", "wb") as berkas:
berkas.write(b"baris satu\r\nbaris dua\r\n")
with open("windows.txt", encoding="utf-8") as berkas:
isi = berkas.read()
print("Mode teks default :", repr(isi))
with open("windows.txt", encoding="utf-8", newline="") as berkas:
isi = berkas.read()
print("Dengan newline='' :", repr(isi))Output yang Dihasilkan:
Mode teks default : 'baris satu\nbaris dua\n'
Dengan newline='' : 'baris satu\r\nbaris dua\r\n'Mari kita bedah blok tersebut:
- Baris 1-2 (
with open("windows.txt", "wb") ...): berkas uji dibuat lewat mode biner"wb"supaya byte\r\ntersimpan persis tanpa diterjemahkan siapa pun. - Baris 4-6 (mode teks default): penerjemahan universal newlines aktif —
\r\ndari disk menjadi\ndi string hasil. - Baris 8-10 (
newline=""): penerjemahan dimatikan; akhir baris mentah\r\nutuh sampai ke tangan kita, persis seperti isinya di disk.
Dengan mode default, \r\n diterjemahkan menjadi \n — program kita tidak perlu peduli asal berkasnya. Dengan newline="", penerjemahan dimatikan dan akhir baris mentah \r\n utuh dibaca apa adanya. Saat menulis, kebalikannya berlaku: argument newline tertentu seperti "\r\n" menerjemahkan setiap \n yang kita tulis menjadi string tersebut:
with open("gaya-windows.txt", "w", encoding="utf-8", newline="\r\n") as berkas:
berkas.write("baris satu\nbaris dua\n")
with open("gaya-windows.txt", "rb") as berkas:
print(berkas.read())Output yang Dihasilkan:
b'baris satu\r\nbaris dua\r\n'Pembacaan memakai mode biner membuktikan berkas di disk benar-benar berisi \r\n — berguna saat kita harus menghasilkan berkas untuk program Windows lawas.
Mode Teks vs Mode Biner #
Rangkuman perbedaan kedua mode sebelum kita melangkah lebih jauh:
| Aspek | Mode teks ("r", "w") | Mode biner ("rb", "wb") |
|---|---|---|
Hasil read() | str | bytes |
Argument encoding | berlaku, wajib eksplisit | tidak boleh dipakai |
| Penerjemahan newline | aktif (bisa dimatikan dengan newline="") | tidak ada |
| Cocok untuk | teks yang dibaca manusia | gambar, audio, arsip, semua data mentah |
Demo singkat memperlihatkan tipe data yang berbeda untuk berkas yang sama:
with open("teks.txt", "w", encoding="utf-8") as berkas:
berkas.write("Halo\n")
with open("teks.txt", encoding="utf-8") as berkas:
print(type(berkas.read()).__name__)
with open("teks.txt", "rb") as berkas:
print(type(berkas.read()).__name__)Output yang Dihasilkan:
str
bytesMode teks mengembalikan str yang sudah diterjemahkan dari byte; mode biner mengembalikan bytes mentah tanpa penafsiran. Pekerjaan dengan bytes secara penuh dibahas pada Chapter Bekerja dengan File Biner.