File
Paket standar yang disediakan Go untuk penanganan file kira-kira beberapa berikut:
- Paket
os, bertanggung jawab atas implementasi spesifik interaksi sistem file OS - Paket
io, lapisan abstraksi untuk IO baca-tulis - Paket
fs, lapisan abstraksi untuk sistem file
Artikel ini akan menjelaskan cara melakukan penanganan file dasar dalam bahasa Go.
Membuka
Dua cara umum untuk membuka file adalah menggunakan dua fungsi yang disediakan paket os, fungsi Open mengembalikan pointer file dan error,
func Open(name string) (*File, error)Fungsi OpenFile yang kedua dapat memberikan kontrol yang lebih granular, fungsi Open adalah enkapsulasi sederhana dari fungsi OpenFile.
func OpenFile(name string, flag int, perm FileMode) (*File, error)Pertama perkenalkan cara penggunaan pertama, cukup sediakan nama file yang sesuai, kode sebagai berikut
func main() {
file, err := os.Open("README.txt")
fmt.Println(file, err)
}Path pencarian file default adalah path folder go.mod proyek, karena di bawah proyek tidak ada file README.txt, tentu akan mengembalikan error.
<nil> open README.txt: The system cannot find the file specified.Karena tipe error IO ada banyak, perlu manual menentukan apakah file ada, sama juga paket os juga menyediakan fungsi kemudahan untuk ini, kode yang dimodifikasi sebagai berikut
func main() {
file, err := os.Open("README.txt")
if os.IsNotExist(err) {
fmt.Println("File tidak ada")
} else if err != nil {
fmt.Println("Akses file abnormal")
} else {
fmt.Println("Baca file berhasil", file)
}
}Jalankan lagi output sebagai berikut
File tidak adaSebenarnya fungsi pertama hanya membaca file read-only, tidak dapat dimodifikasi
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}Melalui fungsi OpenFile dapat mengontrol lebih banyak detail, seperti memodifikasi file descriptor dan permission file. Tentang file descriptor, paket os menyediakan konstanta berikut untuk digunakan.
const (
// Hanya baca, hanya tulis, baca tulis tiga jenis harus menentukan satu
O_RDONLY int = syscall.O_RDONLY // Buka file dengan mode hanya baca
O_WRONLY int = syscall.O_WRONLY // Buka file dengan mode hanya tulis
O_RDWR int = syscall.O_RDWR // Buka file dengan mode baca tulis
// Nilai sisa digunakan untuk mengontrol perilaku
O_APPEND int = syscall.O_APPEND // Saat menulis file, tambahkan data ke akhir file
O_CREATE int = syscall.O_CREAT // Buat file jika file tidak ada
O_EXCL int = syscall.O_EXCL // Gunakan dengan O_CREATE, file harus tidak ada
O_SYNC int = syscall.O_SYNC // Buka file dengan mode IO sinkron
O_TRUNC int = syscall.O_TRUNC // Truncate file yang dapat ditulis saat membuka
)Tentang permission file menyediakan konstanta berikut.
const (
ModeDir = fs.ModeDir // d: direktori
ModeAppend = fs.ModeAppend // a: hanya tambah
ModeExclusive = fs.ModeExclusive // l: eksklusif
ModeTemporary = fs.ModeTemporary // T: file sementara
ModeSymlink = fs.ModeSymlink // L: symbolic link
ModeDevice = fs.ModeDevice // D: file device
ModeNamedPipe = fs.ModeNamedPipe // p: named pipe (FIFO)
ModeSocket = fs.ModeSocket // S: Unix domain socket
ModeSetuid = fs.ModeSetuid // u: setuid
ModeSetgid = fs.ModeSetgid // g: setgid
ModeCharDevice = fs.ModeCharDevice // c: Unix character device, prasyarat ModeDevice diset
ModeSticky = fs.ModeSticky // t: sticky bit
ModeIrregular = fs.ModeIrregular // ?: file tidak teratur
// Masker bit tipe. Untuk file reguler, tidak ada yang diset.
ModeType = fs.ModeType
ModePerm = fs.ModePerm // Bit permission Unix, 0o777
)Berikut adalah contoh kode membuka file dengan mode baca tulis, permission 0666,表示 semua orang dapat membaca dan menulis file ini, dan akan otomatis dibuat saat tidak ada.
func main() {
file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE, 0666)
if os.IsNotExist(err) {
fmt.Println("File tidak ada")
} else if err != nil {
fmt.Println("Akses file abnormal")
} else {
fmt.Println("Buka file berhasil", file.Name())
file.Close()
}
}Output sebagai berikut
Buka file berhasil README.txtJika hanya ingin mendapatkan beberapa informasi file ini, tidak ingin membaca file ini, dapat menggunakan fungsi os.Stat() untuk operasi, contoh kode sebagai berikut
func main() {
fileInfo, err := os.Stat("README.txt")
if err != nil {
fmt.Println(err)
} else {
fmt.Println(fmt.Sprintf("%+v", fileInfo))
}
}Output sebagai berikut
&{name:README.txt FileAttributes:32 CreationTime:{LowDateTime:3603459389 HighDateTime:31016791} LastAccessTime:{LowDateTime:3603459389 HighDateTime:31016791} LastWriteTime:{LowDateTime:3603459389 HighDateTime:31016791} FileSizeHigh
:0 FileSizeLow:0 Reserved0:0 filetype:0 Mutex:{state:0 sema:0} path:README.txt vol:0 idxhi:0 idxlo:0 appendNameToPath:false}WARNING
Setelah membuka file selalu ingat untuk menutup file ini, biasanya operasi tutup akan diletakkan di statement defer
defer file.Close()Membaca
Setelah berhasil membuka file, dapat melakukan operasi baca. Tentang operasi baca file, tipe *os.File menyediakan beberapa metode publik berikut
// Baca file ke dalam slice byte yang传入
func (f *File) Read(b []byte) (n int, err error)
// Dibandingkan yang pertama dapat membaca dari offset yang ditentukan
func (f *File) ReadAt(b []byte, off int64) (n int, err error)Sebagian besar kasus yang pertama lebih banyak digunakan. Untuk metode pertama, perlu menulis logika sendiri untuk melakukan ekspansi dinamis slice saat membaca, kode sebagai berikut
func ReadFile(file *os.File) ([]byte, error) {
buffer := make([]byte, 0, 512)
for {
// Saat kapasitas tidak cukup
if len(buffer) == cap(buffer) {
// Ekspansi
buffer = append(buffer, 0)[:len(buffer)]
}
// Lanjut baca file
offset, err := file.Read(buffer[len(buffer):cap(buffer)])
// Masukkan data yang sudah ditulis ke slice
buffer = buffer[:len(buffer)+offset]
// Saat terjadi error
if err != nil {
if errors.Is(err, io.EOF) {
err = nil
}
return buffer, err
}
}
}Logika sisa sebagai berikut
func main() {
file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("Akses file abnormal")
} else {
fmt.Println("Buka file berhasil", file.Name())
bytes, err := ReadFile(file)
if err != nil {
fmt.Println("Baca file abnormal", err)
} else {
fmt.Println(string(bytes))
}
file.Close()
}
}Output adalah
Buka file berhasil README.txt
hello world!Selain itu, dapat juga menggunakan dua fungsi kemudahan untuk membaca file, masing-masing adalah fungsi ReadFile di paket os, dan fungsi ReadAll di paket io. Untuk os.ReadFile, cukup sediakan path file, sedangkan untuk io.ReadAll, perlu menyediakan implementasi tipe io.Reader.
os.ReadFile
func ReadFile(name string) ([]byte, error)Contoh penggunaan sebagai berikut
func main() {
bytes, err := os.ReadFile("README.txt")
if err != nil {
fmt.Println(err)
} else {
fmt.Println(string(bytes))
}
}Output sebagai berikut
hello world!io.ReadAll
func ReadAll(r Reader) ([]byte, error)Contoh penggunaan sebagai berikut
func main() {
file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("Akses file abnormal")
} else {
fmt.Println("Buka file berhasil", file.Name())
bytes, err := io.ReadAll(file)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(string(bytes))
}
file.Close()
}
}Buka file berhasil README.txt
hello world!Menulis
Struct os.File menyediakan beberapa metode berikut untuk menulis data
// Tulis slice byte
func (f *File) Write(b []byte) (n int, err error)
// Tulis string
func (f *File) WriteString(s string) (n int, err error)
// Mulai menulis dari posisi yang ditentukan, saat dibuka dengan mode os.O_APPEND akan mengembalikan error
func (f *File) WriteAt(b []byte, off int64) (n int, err error)Jika ingin menulis data ke file, harus membuka dengan mode O_WRONLY atau O_RDWR, jika tidak tidak akan berhasil menulis file. Berikut adalah contoh membuka file dengan mode os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC, dan permission 0666 untuk menulis data yang ditentukan
func main() {
file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0666)
if err != nil {
fmt.Println("Akses file abnormal")
} else {
fmt.Println("Buka file berhasil", file.Name())
for i := 0; i < 5; i++ {
offset, err := file.WriteString("hello world!\n")
if err != nil {
fmt.Println(offset, err)
}
}
fmt.Println(file.Close())
}
}Karena membuka file dengan mode os.O_APPEND, saat menulis file akan menambahkan data ke akhir file, setelah eksekusi selesai konten file sebagai berikut
hello world!
hello world!
hello world!
hello world!
hello world!Menulis slice byte ke file juga operasi serupa, tidak akan diulang lagi. Untuk operasi menulis file paket standar juga menyediakan fungsi kemudahan, masing-masing adalah os.WriteFile dan io.WriteString
os.WriteFile
func WriteFile(name string, data []byte, perm FileMode) errorContoh penggunaan sebagai berikut
func main() {
err := os.WriteFile("README.txt", []byte("hello world!\n"), 0666)
if err != nil {
fmt.Println(err)
}
}Saat ini konten file sebagai berikut
hello world!io.WriteString
func WriteString(w Writer, s string) (n int, err error)Contoh penggunaan sebagai berikut
func main() {
file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0666)
if err != nil {
fmt.Println("Akses file abnormal")
} else {
fmt.Println("Buka file berhasil", file.Name())
for i := 0; i < 5; i++ {
offset, err := io.WriteString(file, "hello world!\n")
if err != nil {
fmt.Println(offset, err)
}
}
fmt.Println(file.Close())
}
}hello world!
hello world!
hello world!
hello world!
hello world!Fungsi os.Create digunakan untuk membuat file, pada dasarnya juga enkapsulasi dari OpenFile.
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}WARNING
Saat membuat file, jika direktori parent tidak ada, akan gagal membuat dan mengembalikan error.
Menyalin
Untuk menyalin file, perlu membuka dua file sekaligus, metode pertama adalah membaca data dari file asli, lalu menulis ke file target, contoh kode sebagai berikut
func main() {
// Baca data dari file asli
data, err := os.ReadFile("README.txt")
if err != nil {
fmt.Println(err)
return
}
// Tulis file target
err = os.WriteFile("README(1).txt", data, 0666)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Salin berhasil")
}
}*os.File.ReadFrom
Metode lain adalah menggunakan metode ReadFrom yang disediakan os.File, saat membuka file, satu hanya baca, satu hanya tulis.
func (f *File) ReadFrom(r io.Reader) (n int64, err error)Contoh penggunaan sebagai berikut
func main() {
// Buka file asli dengan hanya baca
origin, err := os.OpenFile("README.txt", os.O_RDONLY, 0666)
if err != nil {
fmt.Println(err)
return
}
defer origin.Close()
// Buka file salinan dengan hanya tulis
target, err := os.OpenFile("README(1).txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
fmt.Println(err)
return
}
defer target.Close()
// Baca data dari file asli, lalu tulis ke file salinan
offset, err := target.ReadFrom(origin)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Salin file berhasil", offset)
}Cara menyalin ini perlu membaca semua konten source file ke memori terlebih dahulu, lalu menulis ke file target, tidak disarankan melakukan ini saat file sangat besar.
io.Copy
Metode lain adalah menggunakan fungsi io.Copy, fungsi ini baca sambil tulis, pertama baca konten ke buffer, lalu tulis ke file target, ukuran buffer default adalah 32KB.
func Copy(dst Writer, src Reader) (written int64, err error)Contoh penggunaan sebagai berikut
func main() {
// Buka file asli dengan hanya baca
origin, err := os.OpenFile("README.txt", os.O_RDONLY, 0666)
if err != nil {
fmt.Println(err)
return
}
defer origin.Close()
// Buka file salinan dengan hanya tulis
target, err := os.OpenFile("README(1).txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
fmt.Println(err)
return
}
defer target.Close()
// Salin
written, err := io.Copy(target, origin)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(written)
}
}Anda juga dapat menggunakan io.CopyBuffer untuk menentukan ukuran buffer.
Mengganti Nama
Mengganti nama juga dapat dipahami sebagai memindahkan file, akan menggunakan fungsi Rename di paket os.
func Rename(oldpath, newpath string) errorContoh sebagai berikut
func main() {
err := os.Rename("README.txt", "readme.txt")
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Ganti nama berhasil")
}
}Fungsi ini untuk folder juga efek yang sama.
Menghapus
Operasi hapus jauh lebih sederhana dibandingkan operasi lain, hanya akan menggunakan dua fungsi di paket os
// Hapus file tunggal atau direktori kosong, saat direktori tidak kosong akan mengembalikan error
func Remove(name string) error
// Hapus semua file dan direktori di direktori yang ditentukan termasuk sub direktori dan sub file
func RemoveAll(path string) errorPenggunaannya sangat sederhana, berikut adalah contoh menghapus direktori
func main() {
// Hapus semua file dan sub direktori di direktori saat ini
err := os.RemoveAll(".")
if err != nil {
fmt.Println(err)
}else {
fmt.Println("Hapus berhasil")
}
}Berikut adalah contoh menghapus file tunggal
func main() {
// Hapus semua file dan sub direktori di direktori saat ini
err := os.Remove("README.txt")
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Hapus berhasil")
}
}Flush
Fungsi os.Sync adalah enkapsulasi dari system call底层 Fsync, digunakan untuk menerapkan IO yang di-cache di sistem operasi ke disk
func main() {
create, err := os.Create("test.txt")
if err != nil {
panic(err)
}
defer create.Close()
_, err = create.Write([]byte("hello"))
if err != nil {
panic(err)
}
// Flush ke disk
if err := create.Sync();err != nil {
return
}
}Folder
Banyak operasi folder mirip dengan operasi file
Membaca
Untuk folder, ada dua cara membuka,
os.ReadDir
Cara pertama adalah menggunakan fungsi os.ReadDir
func ReadDir(name string) ([]DirEntry, error)func main() {
// Direktori saat ini
dir, err := os.ReadDir(".")
if err != nil {
fmt.Println(err)
} else {
for _, entry := range dir {
fmt.Println(entry.Name())
}
}
}*os.File.ReadDir
Cara kedua adalah menggunakan fungsi *os.File.ReadDir, os.ReadDir pada dasarnya hanya enkapsulasi sederhana dari *os.File.ReadDir.
// Saat n < 0, baca semua konten di folder
func (f *File) ReadDir(n int) ([]DirEntry, error)func main() {
// Direktori saat ini
dir, err := os.Open(".")
if err != nil {
fmt.Println(err)
}
defer dir.Close()
dirs, err := dir.ReadDir(-1)
if err != nil {
fmt.Println(err)
} else {
for _, entry := range dirs {
fmt.Println(entry.Name())
}
}
}Membuat
Operasi membuat folder akan menggunakan dua fungsi di paket os
// Buat direktori dengan nama yang ditentukan menggunakan permission yang ditentukan
func Mkdir(name string, perm FileMode) error
// Dibandingkan yang pertama fungsi ini akan membuat semua direktori parent yang diperlukan
func MkdirAll(path string, perm FileMode) errorContoh sebagai berikut
func main() {
err := os.Mkdir("src", 0666)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Buat berhasil")
}
}Menyalin
Kita dapat menulis fungsi sendiri untuk recursively traversing seluruh folder, tetapi paket standar filepath sudah menyediakan fungsi dengan fungsi serupa, oleh karena itu dapat langsung digunakan, contoh kode sederhana untuk menyalin folder sebagai berikut.
func CopyDir(src, dst string) error {
// Periksa status folder source
_, err := os.Stat(src)
if err != nil {
return err
}
return filepath.Walk(src, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
// Hitung path relatif
rel, err := filepath.Rel(src, path)
if err != nil {
return err
}
// Gabungkan path target
destpath := filepath.Join(dst, rel)
// Buat folder
var dirpath string
var mode os.FileMode = 0755
if info.IsDir() {
dirpath = destpath
mode = info.Mode()
} else if info.Mode().IsRegular() {
dirpath = filepath.Dir(destpath)
}
if err := os.MkdirAll(dirpath, mode); err != nil {
return err
}
// Buat file
if info.Mode().IsRegular() {
srcfile, err := os.Open(path)
if err != nil {
return err
}
// Selalu ingat untuk menutup file
defer srcfile.Close()
destfile, err := os.OpenFile(destpath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, info.Mode())
if err != nil {
return err
}
defer destfile.Close()
// Salin konten file
if _, err := io.Copy(destfile, srcfile); err != nil {
return err
}
return nil
}
return nil
})
}filepath.Walk akan recursively traversing seluruh folder, dalam proses, bertemu folder buat folder, bertemu file buat file baru dan salin, kode dibandingkan menyalin file sedikit banyak tetapi tidak terlalu kompleks.
