Работа с файловой системой в Go: полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с файловой системой в Go: от основ до продвинутых техник

Go (Golang) — это современный компилируемый язык программирования, который славится своей производительностью, простотой и встроенной поддержкой работы с файловой системой. Возможность эффективно читать, записывать, создавать и управлять файлами и директориями — один из ключевых навыков для любого разработчика на Go. В этой статье мы подробно разберем основные операции с файловой системой, используя стандартную библиотеку os, io/ioutil (начиная с Go 1.16 рекомендуется использовать os и io) и path/filepath. Вы узнаете, как открывать и закрывать файлы, читать и записывать данные, работать с директориями и обходить файловые деревья.

1. Основы: открытие, чтение и запись файлов

Работа с файлами в Go начинается с пакета os. Чтобы открыть файл, используется функция os.Open (только для чтения) или os.OpenFile (для чтения и записи с указанием режима). После завершения работы файл обязательно нужно закрыть с помощью defer file.Close(), чтобы освободить системные ресурсы.

Чтение файла целиком

Самый простой способ прочитать файл — использовать os.ReadFile (доступно с Go 1.16). Он возвращает срез байтов и ошибку.

package main

import (
    "fmt"
    "os"
)

func main() {
    data, err := os.ReadFile("example.txt")
    if err != nil {
        fmt.Println("Ошибка чтения файла:", err)
        return
    }
    fmt.Println("Содержимое файла:")
    fmt.Println(string(data))
}

Запись в файл

Для записи данных в файл используется os.WriteFile. Он создает или перезаписывает файл.

package main

import (
    "os"
)

func main() {
    content := []byte("Привет, мир!\nЭто строка для записи.")
    err := os.WriteFile("output.txt", content, 0644)
    if err != nil {
        panic(err)
    }
}

Обратите внимание на третий аргумент — 0644. Это права доступа к файлу в Unix-стиле (владелец может читать и писать, остальные — только читать).

Чтение и запись с буферизацией

Для больших файлов или потоковой обработки используйте bufio:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("large.txt")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
    if err := scanner.Err(); err != nil {
        fmt.Println("Ошибка сканирования:", err)
    }
}

2. Работа с директориями

Go предоставляет удобные функции для создания, удаления и чтения директорий. Основные из них находятся в пакете os.

Создание директории

Для создания одной директории используйте os.Mkdir, для вложенных — os.MkdirAll.

package main

import (
    "os"
)

func main() {
    // Создаем одну директорию
    err := os.Mkdir("newdir", 0755)
    if err != nil {
        panic(err)
    }

    // Создаем вложенные директории
    err = os.MkdirAll("path/to/nested/dir", 0755)
    if err != nil {
        panic(err)
    }
}

Чтение содержимого директории

Функция os.ReadDir возвращает список элементов в директории (начиная с Go 1.16).

package main

import (
    "fmt"
    "os"
)

func main() {
    entries, err := os.ReadDir(".")
    if err != nil {
        panic(err)
    }

    for _, entry := range entries {
        fmt.Println(entry.Name(), entry.IsDir())
    }
}

Удаление файлов и директорий

Для удаления используйте os.Remove (для файла или пустой директории) и os.RemoveAll (для директории со всем содержимым).

package main

import (
    "os"
)

func main() {
    // Удалить файл
    err := os.Remove("temp.txt")
    if err != nil {
        panic(err)
    }

    // Удалить директорию со всем содержимым
    err = os.RemoveAll("old_project")
    if err != nil {
        panic(err)
    }
}

3. Обход файловой системы

Часто требуется рекурсивно обойти все файлы и папки в директории. Для этого в Go есть пакет path/filepath с функцией filepath.Walk (или более производительная filepath.WalkDir с Go 1.16).

Использование filepath.WalkDir

Функция принимает корневую директорию и callback-функцию, которая вызывается для каждого элемента.

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func main() {
    root := "."
    err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
        if err != nil {
            return err
        }
        fmt.Printf("Путь: %s, Размер: ", path)
        if !d.IsDir() {
            info, _ := d.Info()
            fmt.Printf("%d байт", info.Size())
        } else {
            fmt.Print("[директория]")
        }
        fmt.Println()
        return nil
    })
    if err != nil {
        fmt.Println("Ошибка обхода:", err)
    }
}

Этот пример выводит все файлы и папки, начиная с текущей директории, с указанием размера для файлов. WalkDir быстрее, чем Walk, так как не вызывает os.Lstat для каждого элемента, если это не нужно.

4. Продвинутые техники: проверка существования и работа с временными файлами

Помимо базовых операций, часто нужно проверять, существует ли файл, или создавать временные файлы для безопасной работы.

Проверка существования файла

Используйте os.Stat и проверяйте ошибку с помощью errors.Is(err, os.ErrNotExist).

package main

import (
    "errors"
    "fmt"
    "os"
)

func fileExists(filename string) bool {
    _, err := os.Stat(filename)                

Блоги

Рекомендаций по книгам