Menu

Membaca dan Menulis File di Golang: os, bufio, dan Direktori

Cara membaca dan menulis file di Go: os.ReadFile dan os.WriteFile, membaca baris demi baris dengan bufio.Scanner, menambahkan isi dengan os.OpenFile, memeriksa apakah file ada, dan bekerja dengan direktori.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Membaca dan menulis seluruh isi file

os.WriteFile dan os.ReadFile sudah mencakup kebanyakan kebutuhan. Keduanya membuka, menulis atau membaca, lalu menutup file dalam satu pemanggilan.

Contoh-contoh di halaman ini bekerja di direktori sementara dari os.MkdirTemp dan menghapusnya dengan defer os.RemoveAll(dir), jadi tidak ada yang tertinggal. Di kodemu sendiri, path relatif seperti "config.json" dicari berdasarkan direktori kerja proses, yang belum tentu sama dengan direktori file sumber atau binary-nya.

os.WriteFile membuat file kalau perlu dan mengosongkannya (truncate) kalau file itu sudah ada. Argumen ketiga adalah izin Unix untuk file yang baru dibuat: 0o644 berarti pemiliknya bisa membaca dan menulis, orang lain hanya bisa membaca. Argumen ini diabaikan untuk file yang sudah ada, dan umask proses bisa menghapus sebagian bit-nya.

os.ReadFile membaca semuanya ke memori. Itu tepat untuk file konfigurasi dan input kecil, dan salah untuk log berukuran beberapa gigabyte.

Membaca baris demi baris dengan bufio.Scanner

Untuk file besar, atau saat kamu memang ingin bekerja per baris, pakai bufio.Scanner. Scanner membaca per potongan dan memberimu satu baris setiap kali, tanpa karakter baris baru.

Tiga detail:

  • Periksa sc.Err() setelah perulangan. Scan mengembalikan false baik di akhir file maupun saat terjadi error, dan hanya Err yang bisa membedakannya.
  • Batas baris 64 KB. Secara default, satu baris yang lebih panjang dari 64 KB menghentikan scanner dengan bufio.Scanner: token too long. Untuk file dengan baris panjang (JSON yang di-minify, sebagian log), naikkan batasnya sebelum perulangan: sc.Buffer(make([]byte, 1024*1024), 10*1024*1024).
  • Satuan lain. sc.Split(bufio.ScanWords) menghasilkan kata; bufio.ScanRunes menghasilkan karakter.

Untuk membaca stream dalam potongan berukuran tetap alih-alih per baris, pakai f.Read(buf) di dalam perulangan atau io.Copy ke writer lain.

os.Create(name) membuka file untuk ditulis, dengan membuat atau mengosongkannya. os.OpenFile memberi kendali penuh lewat flag:

FlagArti
os.O_RDONLY, os.O_WRONLY, os.O_RDWRbuka untuk membaca, menulis, atau keduanya (pilih satu)
os.O_CREATEbuat file kalau belum ada
os.O_TRUNCkosongkan file saat dibuka
os.O_APPENDsetiap penulisan masuk ke bagian akhir
os.O_EXCLbersama O_CREATE: gagal kalau file sudah ada

os.Open(name) sama dengan OpenFile(name, O_RDONLY, 0). os.Create(name) sama dengan OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0o666).

Saat menulis, error dari Close itu penting. Sebagian file system baru melaporkan kegagalan penulisan saat file ditutup, jadi defer f.Close() saja bisa menyembunyikan data yang hilang. Untuk file yang kamu tulis, periksa Close secara eksplisit seperti yang dilakukan appendLine. Untuk file yang hanya kamu baca, defer f.Close() tidak masalah.

fmt.Fprintln dan fungsi cetak fmt lainnya menerima io.Writer apa pun, termasuk file. bufio.Writer mengumpulkan penulisan kecil di memori. Lupa memanggil w.Flush() adalah bug klasik: program selesai dengan normal dan beberapa kilobyte terakhir tidak pernah sampai ke file.

Apakah file itu ada?

Go tidak punya os.Exists. Panggil os.Stat dan periksa error-nya:

errors.Is(err, fs.ErrNotExist) adalah idiom yang berlaku sekarang. Idiom ini menggantikan os.IsNotExist(err) yang lebih lama, yang tidak bisa melihat ke dalam error yang dibungkus.

Memeriksa sebelum membuka sering kali tidak perlu dan rawan race: file bisa muncul atau hilang di antara pemeriksaan dan pembukaan. Biasanya kamu cukup membukanya dan menangani fs.ErrNotExist dari pembukaan itu. Untuk membuat file hanya kalau file itu belum ada, pakai O_CREATE|O_EXCL, yang menjadikan pemeriksaan dan pembuatan satu langkah atomik.

Direktori

TugasFungsi
membuat satu direktorios.Mkdir(path, 0o755)
membuat path beserta induknyaos.MkdirAll(path, 0o755)
menampilkan isi direktorios.ReadDir(path)
menelusuri pohon direktorifilepath.WalkDir(root, fn)
menghapus file atau direktori kosongos.Remove(path)
menghapus seluruh pohonos.RemoveAll(path)
mengganti nama atau memindahkanos.Rename(old, new)
file atau direktori sementaraos.CreateTemp("", "prefix-*"), os.MkdirTemp("", "prefix")
menggabungkan bagian pathfilepath.Join(a, b, c)

Pakai path/filepath untuk path file system: package ini memakai pemisah yang tepat untuk sistem operasinya (\ di Windows). Package path dipakai untuk path yang dipisahkan garis miring, seperti URL.

Go 1.24 juga menambahkan os.Root (os.OpenRoot(dir)), yang hanya membuka file di dalam satu direktori dan menolak path yang keluar darinya lewat .. atau symlink. Pakai saat nama file berasal dari pengguna.

Kesalahan umum

  • Tidak memeriksa error. Setiap pemanggilan ini bisa gagal. File yang gagal dibuka bernilai nil, dan setiap Read, Write, atau Close setelahnya mengembalikan invalid argument, yang menyembunyikan penyebab sebenarnya (file-nya tidak ada, atau izinnya ditolak).
  • Lupa sc.Err() setelah perulangan scan. Error saat membaca terlihat seperti akhir file.
  • Lupa Flush pada bufio.Writer. Bagian akhir file hilang.
  • Mengabaikan error dari Close setelah menulis. Error penulisan bisa saja baru muncul di sana.
  • defer f.Close() di dalam perulangan atas banyak file. File-file itu tetap terbuka sampai fungsinya selesai dan kamu bisa kehabisan file descriptor. Pindahkan badan perulangan ke sebuah fungsi supaya setiap file ditutup per iterasi.
  • Izin akses yang ditulis sebagai desimal. 644 tidak sama dengan 0o644. Go membaca 644 sebagai angka desimal, yaitu 0o1204, dan mengisi bit-bit yang aneh.

Pertanyaan yang sering diajukan

Bagaimana cara membaca seluruh isi file menjadi string di Go?

data, err := os.ReadFile("notes.txt") mengembalikan isinya sebagai []byte; konversi dengan string(data). Fungsi ini membuka, membaca, dan menutup file untukmu. Pakai untuk file yang muat dengan nyaman di memori; untuk file besar, baca baris demi baris dengan bufio.Scanner.

Bagaimana cara membaca file baris demi baris di Go?

Buka file dengan os.Open, defer f.Close(), bungkus dengan bufio.NewScanner(f), lakukan perulangan dengan for sc.Scan() { line := sc.Text() }, dan periksa sc.Err() setelah perulangan. Baris yang lebih panjang dari 64 KB membuat scanner gagal dengan token too long kecuali kamu memperbesar buffer-nya dengan sc.Buffer.

Bagaimana cara menambahkan isi ke file (append) di Go?

Buka dengan os.OpenFile(name, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644), tulis, lalu periksa error dari Close. O_CREATE membuat file kalau belum ada, dan O_APPEND membuat setiap penulisan masuk ke bagian akhir.

Bagaimana cara memeriksa apakah sebuah file ada di Go?

Panggil os.Stat(path) dan uji error-nya dengan errors.Is(err, fs.ErrNotExist). Error yang nil berarti file itu ada. Error lain apa pun (misalnya permission denied) berarti kamu tidak bisa memastikannya, jadi tangani secara terpisah alih-alih menganggapnya "tidak ada".

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai