Работа с файловой системой в Go: для начинающих

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

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

Начать курс

Работа с файловой системой в Go: для начинающих

Go (Golang) — это современный компилируемый язык, который славится своей простотой, производительностью и встроенными инструментами. Одной из ключевых задач любого backend-разработчика является взаимодействие с файловой системой: чтение конфигураций, запись логов, создание временных файлов и директорий. В этой статье мы разберём основные операции с файлами и папками на Go, начиная с самых азов. Материал рассчитан на новичков, поэтому каждый шаг будет сопровождаться понятными примерами кода.

Основные пакеты для работы с файлами

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

  • os — низкоуровневая работа с файлами, папками, правами доступа и процессами.
  • io/ioutil (устарел в Go 1.16, но всё ещё встречается) — удобные функции для чтения/записи файлов целиком.
  • io — интерфейсы для потокового ввода/вывода.
  • bufio — буферизированное чтение/запись (полезно для больших файлов).

Начиная с Go 1.16, рекомендуется использовать функции из пакета os (os.ReadFile, os.WriteFile) вместо ioutil. Мы будем опираться на современный подход.

Чтение файлов

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

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))
}

Если файл большой (например, лог-файл на сотни мегабайт), лучше читать его построчно или блоками с помощью bufio.Scanner:

package main

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

func main() {
    file, err := os.Open("bigfile.log")
    if err != nil {
        fmt.Println("Ошибка открытия файла:", err)
        return
    }
    defer file.Close() // обязательно закрываем файл

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        line := scanner.Text()
        fmt.Println(line) // обрабатываем строку
    }

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

Обратите внимание на defer file.Close() — это гарантирует, что файл будет закрыт после завершения функции, даже если произойдёт ошибка.

Запись в файлы

Для записи данных в файл используйте os.WriteFile (создаёт/перезаписывает файл) или os.Create + Write (для более гибкой записи).

package main

import (
    "fmt"
    "os"
)

func main() {
    // Простая запись строки в файл
    text := "Привет, мир!\nВторая строка."
    err := os.WriteFile("output.txt", []byte(text), 0644)
    if err != nil {
        fmt.Println("Ошибка записи:", err)
        return
    }
    fmt.Println("Файл успешно записан")
}

Параметр 0644 — это права доступа в Unix-стиле (владелец может читать/писать, остальные — только читать).

Если нужно дописать данные в конец файла (append), используйте os.OpenFile с флагом os.O_APPEND:

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        fmt.Println("Ошибка открытия файла:", err)
        return
    }
    defer file.Close()

    _, err = file.WriteString("Новая запись в лог\n")
    if err != nil {
        fmt.Println("Ошибка записи:", err)
    }
}

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

Go позволяет легко создавать, переименовывать и удалять папки. Рассмотрим базовые операции.

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

package main

import (
    "fmt"
    "os"
)

func main() {
    // Создание одной директории
    err := os.Mkdir("mydir", 0755)
    if err != nil {
        fmt.Println("Ошибка создания папки:", err)
        return
    }

    // Создание вложенной структуры (как mkdir -p)
    err = os.MkdirAll("parent/child/grandchild", 0755)
    if err != nil {
        fmt.Println("Ошибка создания вложенных папок:", err)
    }
}

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

Получить список файлов и папок внутри директории можно с помощью os.ReadDir:

package main

import (
    "fmt"
    "os"
)

func main() {
    entries, err := os.ReadDir(".")
    if err != nil {
        fmt.Println("Ошибка чтения директории:", err)
        return
    }

    for _, entry := range entries {
        if entry.IsDir() {
            fmt.Printf("[DIR]  %s\n", entry.Name())
        } else {
            fmt.Printf("[FILE] %s\n", entry.Name())
        }
    }
}

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

package main

import (
    "fmt"
    "os"
)

func main() {
    // Удаление одного файла
    err := os.Remove("temp.txt")
    if err != nil {
        fmt.Println("Ошибка удаления файла:", err)
    }

    // Рекурсивное удаление всей папки
    err = os.RemoveAll("tempdir")
    if err != nil {
        fmt.Println("Ошибка удаления папки:", err)
    }
}

Будьте осторожны с os.RemoveAll — он удаляет всё без возможности восстановления!

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

Перед работой с файлом часто нужно проверить, существует ли он. Для этого используйте os.Stat:

package main

import (
    "fmt"
    "os"
)

func main() {
    fileInfo, err := os.Stat("example.txt")
    if os.IsNotExist(err) {
        fmt.Println("Файл не существует")                

Блоги

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