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:

python
import locale

print(locale.getpreferredencoding(False))

Output yang Dihasilkan:

text
UTF-8

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

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

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

python
>>> 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 byte

newline: \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:

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

text
Mode teks default : 'baris satu\nbaris dua\n'
Dengan newline='' : 'baris satu\r\nbaris dua\r\n'

Mari kita bedah blok tersebut:

  1. Baris 1-2 (with open("windows.txt", "wb") ...): berkas uji dibuat lewat mode biner "wb" supaya byte \r\n tersimpan persis tanpa diterjemahkan siapa pun.
  2. Baris 4-6 (mode teks default): penerjemahan universal newlines aktif — \r\n dari disk menjadi \n di string hasil.
  3. Baris 8-10 (newline=""): penerjemahan dimatikan; akhir baris mentah \r\n utuh 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:

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

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

AspekMode teks ("r", "w")Mode biner ("rb", "wb")
Hasil read()strbytes
Argument encodingberlaku, wajib eksplisittidak boleh dipakai
Penerjemahan newlineaktif (bisa dimatikan dengan newline="")tidak ada
Cocok untukteks yang dibaca manusiagambar, audio, arsip, semua data mentah

Demo singkat memperlihatkan tipe data yang berbeda untuk berkas yang sama:

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

text
str
bytes

Mode 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.