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.Scanmengembalikanfalsebaik di akhir file maupun saat terjadi error, dan hanyaErryang 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.ScanRunesmenghasilkan 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.
Menulis: os.Create, os.OpenFile, dan append
os.Create(name) membuka file untuk ditulis, dengan membuat atau mengosongkannya. os.OpenFile memberi kendali penuh lewat flag:
| Flag | Arti |
|---|---|
os.O_RDONLY, os.O_WRONLY, os.O_RDWR | buka untuk membaca, menulis, atau keduanya (pilih satu) |
os.O_CREATE | buat file kalau belum ada |
os.O_TRUNC | kosongkan file saat dibuka |
os.O_APPEND | setiap penulisan masuk ke bagian akhir |
os.O_EXCL | bersama 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
| Tugas | Fungsi |
|---|---|
| membuat satu direktori | os.Mkdir(path, 0o755) |
| membuat path beserta induknya | os.MkdirAll(path, 0o755) |
| menampilkan isi direktori | os.ReadDir(path) |
| menelusuri pohon direktori | filepath.WalkDir(root, fn) |
| menghapus file atau direktori kosong | os.Remove(path) |
| menghapus seluruh pohon | os.RemoveAll(path) |
| mengganti nama atau memindahkan | os.Rename(old, new) |
| file atau direktori sementara | os.CreateTemp("", "prefix-*"), os.MkdirTemp("", "prefix") |
| menggabungkan bagian path | filepath.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 setiapRead,Write, atauClosesetelahnya mengembalikaninvalid 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
Flushpadabufio.Writer. Bagian akhir file hilang. - Mengabaikan error dari
Closesetelah 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.
644tidak sama dengan0o644. Go membaca644sebagai angka desimal, yaitu0o1204, 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".