Dosya
Go dilinin dosya işleme için sağladığı standart kütüphaneler大致 olarak şunlardır:
oskütüphanesi, OS dosya sistemi etkileşiminin somut uygulamasından sorumluduriokütüphanesi, IO okuma/yazma soyutlama katmanıdırfskütüphanesi, dosya sistemi soyutlama katmanıdır
Bu makalede Go dili ile temel dosya işlemenin nasıl yapılacağı anlatılacaktır.
Açma
Dosya açmanın iki yaygın yolu, os paketi tarafından sağlanan iki fonksiyonu kullanmaktır. Open fonksiyonu bir dosya işaretçisi ve bir hata döndürür.
func Open(name string) (*File, error)İkincisi OpenFile daha ince taneli kontrol sağlayabilir. Open fonksiyonu OpenFile fonksiyonunun basit bir sarmalamasıdır.
func OpenFile(name string, flag int, perm FileMode) (*File, error)Önce ilk kullanım yöntemini tanıtalım. Doğrudan ilgili dosya adını sağlayın. Kod şu şekildedir
func main() {
file, err := os.Open("README.txt")
fmt.Println(file, err)
}Dosya arama yolu varsayılan olarak proje go.mod dosyasının bulunduğu yoldur. Proje altında README.txt dosyası olmadığı için doğal olarak bir hata döndürür.
<nil> open README.txt: The system cannot find the file specified.IO hata türleri çok olduğundan, dosyanın var olup olmadığını manuel olarak belirlemeniz gerekir. Aynı şekilde os paketi bunun için kolaylık fonksiyonları sağlar. Değiştirilen kod şu şekildedir
func main() {
file, err := os.Open("README.txt")
if os.IsNotExist(err) {
fmt.Println("Dosya mevcut değil")
} else if err != nil {
fmt.Println("Dosya erişim hatası")
} else {
fmt.Println("Dosya okuma başarılı", file)
}
}Tekrar çalıştırıldığında çıktı şu şekildedir
Dosya mevcut değilAslında ilk fonksiyon sadece salt okunur dosya okur, değiştirilemez
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}OpenFile fonksiyonu ile daha fazla detay kontrol edilebilir. Örneğin dosya tanımlayıcısı ve dosya izinleri değiştirilebilir. Dosya tanımlayıcısı hakkında, os paketi aşağıdaki sabitleri kullanıma sunar.
const (
// Salt okunur, sadece yaz, oku-yaz üçünden biri belirtilmelidir
O_RDONLY int = syscall.O_RDONLY // Dosyayı salt okunur modda aç
O_WRONLY int = syscall.O_WRONLY // Dosyayı sadece yazma modunda aç
O_RDWR int = syscall.O_RDWR // Dosyayı oku-yaz modunda aç
// Kalan değerler davranışı kontrol eder
O_APPEND int = syscall.O_APPEND // Dosya yazarken, veriyi dosya sonuna ekle
O_CREATE int = syscall.O_CREAT // Dosya mevcut değilse oluştur
O_EXCL int = syscall.O_EXCL // O_CREATE ile birlikte kullanılır, dosya mevcut olmamalıdır
O_SYNC int = syscall.O_SYNC // Dosyayı senkron IO modunda aç
O_TRUNC int = syscall.O_TRUNC // Açıldığında yazılabilir dosyayı kes
)Dosya izinleri için aşağıdaki sabitler sağlanır.
const (
ModeDir = fs.ModeDir // d: Dizin
ModeAppend = fs.ModeAppend // a: Sadece ekleme
ModeExclusive = fs.ModeExclusive // l: Özel
ModeTemporary = fs.ModeTemporary // T: Geçici dosya
ModeSymlink = fs.ModeSymlink // L: Sembolik bağlantı
ModeDevice = fs.ModeDevice // D: Aygıt dosyası
ModeNamedPipe = fs.ModeNamedPipe // p: Adlı boru (FIFO)
ModeSocket = fs.ModeSocket // S: Unix alan soketi
ModeSetuid = fs.ModeSetuid // u: setuid
ModeSetgid = fs.ModeSetgid // g: setgid
ModeCharDevice = fs.ModeCharDevice // c: Unix karakter aygıtı, ModeDevice ayarlanmış olmalı
ModeSticky = fs.ModeSticky // t: Yapışkan bit
ModeIrregular = fs.ModeIrregular // ?: Düzensiz dosya
// Tür bitleri maskesi. Normal dosyalar için hiçbir şey ayarlanmaz.
ModeType = fs.ModeType
ModePerm = fs.ModePerm // Unix izin bitleri, 0o777
)Aşağıda bir dosyayı oku-yaz modunda açan kod örneği bulunmaktadır. İzin 0666'dır, yani herkesin dosyayı okuyup yazabileceği anlamına gelir ve mevcut değilse otomatik olarak oluşturulur.
func main() {
file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE, 0666)
if os.IsNotExist(err) {
fmt.Println("Dosya mevcut değil")
} else if err != nil {
fmt.Println("Dosya erişim hatası")
} else {
fmt.Println("Dosya açma başarılı", file.Name())
file.Close()
}
}Çıktı şu şekildedir
Dosya açma başarılı README.txtEğer dosyayı okumak istemiyor, sadece bazı bilgilerini almak istiyorsanız, os.Stat() fonksiyonunu kullanabilirsiniz. Kod örneği şu şekildedir
func main() {
fileInfo, err := os.Stat("README.txt")
if err != nil {
fmt.Println(err)
} else {
fmt.Println(fmt.Sprintf("%+v", fileInfo))
}
}Çıktı şu şekildedir
&{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}::: uyarı
Bir dosyayı açtıktan sonra her zaman dosyayı kapatmayı unutmayın. Genellikle kapatma işlemi defer ifadesine konur
defer file.Close():::
Okuma
Dosya başarıyla açıldıktan sonra, okuma işlemi yapılabilir. Dosya okuma işlemi için, *os.File türü aşağıdaki genel yöntemleri sağlar
// Dosyayı gelen byte slice içine oku
func (f *File) Read(b []byte) (n int, err error)
// İlkine göre belirli bir ofsetten okuyabilir
func (f *File) ReadAt(b []byte, off int64) (n int, err error)Çoğu durumda ilk yöntem daha çok kullanılır. İlk yöntem için, okuma sırasında slice'ın dinamik olarak genişletilmesi için kendi mantığınızı yazmanız gerekir. Kod şu şekildedir
func ReadFile(file *os.File) ([]byte, error) {
buffer := make([]byte, 0, 512)
for {
// Kapasite yetersiz olduğunda
if len(buffer) == cap(buffer) {
// Genişlet
buffer = append(buffer, 0)[:len(buffer)]
}
// Dosyayı okumaya devam et
offset, err := file.Read(buffer[len(buffer):cap(buffer)])
// Yazılan veriyi slice'a dahil et
buffer = buffer[:len(buffer)+offset]
// Hata oluştuğunda
if err != nil {
if errors.Is(err, io.EOF) {
err = nil
}
return buffer, err
}
}
}Kalan mantık şu şekildedir
func main() {
file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("Dosya erişim hatası")
} else {
fmt.Println("Dosya açma başarılı", file.Name())
bytes, err := ReadFile(file)
if err != nil {
fmt.Println("Dosya okuma hatası", err)
} else {
fmt.Println(string(bytes))
}
file.Close()
}
}Çıktı şu şekildedir
Dosya açma başarılı README.txt
hello world!Bunun dışında, dosya okumak için iki kolaylık fonksiyonu kullanabilirsiniz. Bunlar sırasıyla os paketindeki ReadFile fonksiyonu ve io paketindeki ReadAll fonksiyonudur. os.ReadFile için sadece dosya yolunu sağlamanız yeterlidir. io.ReadAll için ise io.Reader türünün bir uygulamasını sağlamanız gerekir.
os.ReadFile
func ReadFile(name string) ([]byte, error)Kullanım örneği şu şekildedir
func main() {
bytes, err := os.ReadFile("README.txt")
if err != nil {
fmt.Println(err)
} else {
fmt.Println(string(bytes))
}
}Çıktı şu şekildedir
hello world!io.ReadAll
func ReadAll(r Reader) ([]byte, error)Kullanım örneği şu şekildedir
func main() {
file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("Dosya erişim hatası")
} else {
fmt.Println("Dosya açma başarılı", file.Name())
bytes, err := io.ReadAll(file)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(string(bytes))
}
file.Close()
}
}Dosya açma başarılı README.txt
hello world!Yazma
os.File yapısı veri yazmak için aşağıdaki yöntemleri sağlar
// Byte slice yaz
func (f *File) Write(b []byte) (n int, err error)
// String yaz
func (f *File) WriteString(s string) (n int, err error)
// Belirli bir konumdan yazmaya başla. os.O_APPEND modunda açıldığında hata döndürür
func (f *File) WriteAt(b []byte, off int64) (n int, err error)Bir dosyaya veri yazmak istiyorsanız, O_WRONLY veya O_RDWR modunda açmalısınız. Aksi takdirde dosyaya başarıyla yazamazsınız. Aşağıda os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC modunda dosya açan ve 0666 izni ile belirtilen veri yazan bir örnek bulunmaktadır
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("Dosya erişim hatası")
} else {
fmt.Println("Dosya açma başarılı", 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())
}
}Dosya os.O_APPEND modunda açıldığından, dosyaya yazılırken veri dosyanın sonuna eklenir. İşlem tamamlandıktan sonra dosya içeriği şu şekildedir
hello world!
hello world!
hello world!
hello world!
hello world!Dosyaya byte slice yazmak da benzer bir işlemdir, tekrar anlatmayacağız. Dosya yazma işlemleri için standart kütüphane kolaylık fonksiyonları sağlar. Bunlar sırasıyla os.WriteFile ve io.WriteString
os.WriteFile
func WriteFile(name string, data []byte, perm FileMode) errorKullanım örneği şu şekildedir
func main() {
err := os.WriteFile("README.txt", []byte("hello world!\n"), 0666)
if err != nil {
fmt.Println(err)
}
}Bu durumda dosya içeriği şu şekildedir
hello world!io.WriteString
func WriteString(w Writer, s string) (n int, err error)Kullanım örneği şu şekildedir
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("Dosya erişim hatası")
} else {
fmt.Println("Dosya açma başarılı", 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!os.Create fonksiyonu dosya oluşturmak için kullanılır. Temelde OpenFile'ın bir sarmalamasıdır.
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}::: uyarı
Bir dosya oluştururken, üst dizini mevcut değilse, oluşturma başarısız olur ve hata döndürür.
:::
Kopyalama
Dosya kopyalama için, iki dosyayı aynı anda açmanız gerekir. İlk yöntem, orijinal dosyadaki veriyi okuyup hedef dosyaya yazmaktır. Kod örneği şu şekildedir
func main() {
// Orijinal dosyadan veri oku
data, err := os.ReadFile("README.txt")
if err != nil {
fmt.Println(err)
return
}
// Hedef dosyaya yaz
err = os.WriteFile("README(1).txt", data, 0666)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Kopyalama başarılı")
}
}*os.File.ReadFrom
Diğer yöntem os.File tarafından sağlanan ReadFrom yöntemini kullanmaktır. Dosyayı açarken, biri salt okunur, diğeri sadece yazma.
func (f *File) ReadFrom(r io.Reader) (n int64, err error)Kullanım örneği şu şekildedir
func main() {
// Orijinal dosyayı salt okunur olarak aç
origin, err := os.OpenFile("README.txt", os.O_RDONLY, 0666)
if err != nil {
fmt.Println(err)
return
}
defer origin.Close()
// Kopya dosyasını sadece yazma olarak aç
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()
// Orijinal dosyadan veri oku, sonra kopya dosyaya yaz
offset, err := target.ReadFrom(origin)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Dosya kopyalama başarılı", offset)
}Bu kopyalama yöntemi önce kaynak dosyanın tüm içeriğini belleğe okur, sonra hedef dosyaya yazar. Dosya çok büyük olduğunda bu önerilmez.
io.Copy
Diğer yöntem io.Copy fonksiyonunu kullanmaktır. Bu bir yandan okurken diğer yandan yazar. Önce içeriği arabelleğe okur, sonra hedef dosyaya yazar. Varsayılan arabellek boyutu 32KB'dır.
func Copy(dst Writer, src Reader) (written int64, err error)Kullanım örneği şu şekildedir
func main() {
// Orijinal dosyayı salt okunur olarak aç
origin, err := os.OpenFile("README.txt", os.O_RDONLY, 0666)
if err != nil {
fmt.Println(err)
return
}
defer origin.Close()
// Kopya dosyasını sadece yazma olarak aç
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()
// Kopyala
written, err := io.Copy(target, origin)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(written)
}
}Arabellek boyutunu belirtmek için io.CopyBuffer kullanabilirsiniz.
Yeniden Adlandırma
Yeniden adlandırma aynı zamanda dosya taşıma olarak da anlaşılabilir. os paketindeki Rename fonksiyonunu kullanır.
func Rename(oldpath, newpath string) errorÖrnek şu şekildedir
func main() {
err := os.Rename("README.txt", "readme.txt")
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Yeniden adlandırma başarılı")
}
}Bu fonksiyon klasörler için de aynı etkiye sahiptir.
Silme
Silme işlemi diğer işlemlere göre çok daha basittir. Sadece os paketindeki iki fonksiyonu kullanır
// Tek bir dosya veya boş dizini sil. Dizin boş değilse hata döndürür
func Remove(name string) error
// Belirtilen dizindeki tüm dosya ve dizinleri alt dizinler ve alt dosyalar dahil sil
func RemoveAll(path string) errorKullanımı çok basittir. Aşağıda dizin silme örneği bulunmaktadır
func main() {
// Mevcut dizindeki tüm dosya ve alt dizinleri sil
err := os.RemoveAll(".")
if err != nil {
fmt.Println(err)
}else {
fmt.Println("Silme başarılı")
}
}Aşağıda tek bir dosya silme örneği bulunmaktadır
func main() {
// Mevcut dizindeki tüm dosya ve alt dizinleri sil
err := os.Remove("README.txt")
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Silme başarılı")
}
}Yenileme
os.Sync fonksiyonu alt sistem çağrısı Fsync'yi sarmalar. İşletim sistemindeki önbelleğe alınmış IO'nun diske yazılmasını sağlar
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)
}
// Diske yaz
if err := create.Sync();err != nil {
return
}
}Klasör
Klasörün birçok işlemi dosya işlemlerine benzer
Okuma
Klasör için, açmanın iki yolu vardır
os.ReadDir
İlk yol os.ReadDir fonksiyonunu kullanmaktır
func ReadDir(name string) ([]DirEntry, error)func main() {
// Mevcut dizin
dir, err := os.ReadDir(".")
if err != nil {
fmt.Println(err)
} else {
for _, entry := range dir {
fmt.Println(entry.Name())
}
}
}*os.File.ReadDir
İkinci yol *os.File.ReadDir fonksiyonunu kullanmaktır. os.ReadDir temelde *os.File.ReadDir'ın basit bir sarmalamasıdır.
// n < 0 olduğunda, klasördeki tüm içeriği oku
func (f *File) ReadDir(n int) ([]DirEntry, error)func main() {
// Mevcut dizin
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())
}
}
}Oluşturma
Klasör oluşturma işlemi os paketindeki iki fonksiyonu kullanır
// Belirtilen izinle belirtilen adlı dizini oluştur
func Mkdir(name string, perm FileMode) error
// İlkine göre bu fonksiyon tüm gerekli üst dizinleri oluşturur
func MkdirAll(path string, perm FileMode) errorÖrnek şu şekildedir
func main() {
err := os.Mkdir("src", 0666)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Oluşturma başarılı")
}
}Kopyalama
Tüm klasörü yinelemeli olarak gezinebiliriz. Ancak filepath standart kütüphanesi benzer işlevsellikte fonksiyonlar sağlar. Bu nedenle doğrudan kullanılabilir. Basit bir klasör kopyalama kod örneği şu şekildedir.
func CopyDir(src, dst string) error {
// Kaynak klasörün durumunu kontrol et
_, 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
}
// Göreli yolu hesapla
rel, err := filepath.Rel(src, path)
if err != nil {
return err
}
// Hedef yolu birleştir
destpath := filepath.Join(dst, rel)
// Klasör oluştur
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
}
// Dosya oluştur
if info.Mode().IsRegular() {
srcfile, err := os.Open(path)
if err != nil {
return err
}
// Dosyayı kapatmayı unutmayın
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()
// Dosya içeriğini kopyala
if _, err := io.Copy(destfile, srcfile); err != nil {
return err
}
return nil
}
return nil
})
}filepath.Walk tüm klasörü yinelemeli olarak gezinir. Süreçte, klasörle karşılaşıldığında klasör oluşturur, dosyayla karşılaşıldığında yeni dosya oluşturur ve kopyalar. Kod dosya kopyalamaya göre biraz daha fazladır ancak karmaşık değildir.
