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

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

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

Начать курс

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

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

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

Начнем с самых распространенных операций. Для открытия файла используется функция os.OpenFile или более простые обертки os.Create и os.Open. Рассмотрим пример чтения всего файла в память:

package main

import (
    "fmt"
    "os"
)

func main() {
    // Чтение всего файла (до Go 1.16 использовали ioutil.ReadFile)
    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("Привет, мир Go!\nВторая строка.")
    err := os.WriteFile("output.txt", content, 0644)
    if err != nil {
        panic(err)
    }
    // Файл output.txt создан и содержит наш текст
}

Если вам нужно дописать данные в конец файла, используйте флаги:

file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
    panic(err)
}
defer file.Close()

if _, err := file.WriteString("Новая запись в лог\n"); err != nil {
    panic(err)
}

2. Работа с директориями: создание, удаление и обход

Управление папками — не менее важная задача. Создать новую директорию можно с помощью os.Mkdir (одна папка) или os.MkdirAll (рекурсивно, как mkdir -p):

package main

import (
    "fmt"
    "os"
)

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

    // Рекурсивно создаем вложенную структуру
    err = os.MkdirAll("data/subdir/logs", 0755)
    if err != nil {
        fmt.Println("Ошибка:", err)
    }
}

Для обхода директории можно использовать функцию os.ReadDir (замена ioutil.ReadDir):

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

for _, entry := range entries {
    fmt.Printf("Имя: %s, Папка: %t\n", entry.Name(), entry.IsDir())
}

А для рекурсивного обхода всех файлов и папок используйте filepath.Walk из пакета path/filepath:

package main

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

func main() {
    err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        fmt.Println(path)
        return nil
    })
    if err != nil {
        fmt.Println("Ошибка обхода:", err)
    }
}

Удаление файлов и папок выполняется функциями os.Remove и os.RemoveAll:

// Удалить один файл или пустую папку
os.Remove("temp.txt")

// Удалить папку со всем содержимым (аналог rm -rf)
os.RemoveAll("data")

3. Продвинутые техники: буферизованный ввод/вывод и работа с временными файлами

Для больших файлов неэффективно читать всё содержимое в память. Используйте буферизованное чтение с помощью bufio:

package main

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

func main() {
    file, err := os.Open("largefile.txt")
    if err != nil {
        panic(err)
    }
    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)
    }
}

Буферизованная запись также проста:

file, _ := os.Create("output.txt")
defer file.Close()

writer := bufio.NewWriter(file)
writer.WriteString("Буферизованная строка\n")
writer.Flush() // Важно! Сбрасываем буфер на диск

Временные файлы и папки создаются с помощью os.CreateTemp и os.MkdirTemp:

tmpFile, err := os.CreateTemp("", "prefix-*.txt")
if err != nil {
    panic(err)
}
defer os.Remove(tmpFile.Name()) // Удаляем после работы

tmpFile.WriteString("Временные данные")
tmpFile.Close()
fmt.Println("Временный файл:", tmpFile.Name())

// Временная папка
tmpDir, _ := os.MkdirTemp("", "mydir-*")
defer os.RemoveAll(tmpDir)

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

Чтобы проверить, существует ли файл, используйте os.Stat в паре с os.IsNotExist:

func fileExists(path string) bool {
    _, err := os.Stat(path)
    return !os.IsNotExist(err)
}

func main() {
    if fileExists("config.yaml") {
        fmt.Println("Файл существует")
    } else {
        fmt.Println("Файл не найден")
    }
}

Получить информацию о файле (размер, время изменения, права доступа) можно через os.Stat:

info, err := os.Stat("example.txt")
if err != nil {
    panic(err)
}

fmt.Println("Размер:", info.Size(), "байт")
fmt.Println("Время изменения:", info.ModTime())
fmt.Println("Это папка?", info.IsDir())
fmt.Println("Права:", info.Mode())

Заключение

Мы рассмотрели клю

Блоги

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