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

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

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

Начать курс

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

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

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

Пакет os предоставляет функции для низкоуровневой работы с файлами. Для чтения всего содержимого файла в память используйте 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(string(data))
}

Для записи данных в файл существует функция os.WriteFile. Она создает файл, если его нет, или перезаписывает существующий.

package main

import (
    "fmt"
    "os"
)

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

Третий аргумент 0644 — это права доступа в Unix-стиле (владелец может читать и писать, остальные — только читать). В Windows этот параметр частично игнорируется.

Потоковое чтение и запись: работа с большими файлами

Для работы с большими файлами (более нескольких мегабайт) не стоит загружать их в память целиком. Используйте буферизированное чтение через bufio или пакет io.

package main

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

func main() {
    file, err := os.Open("large_file.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)
    }
}

Для потоковой записи используйте bufio.NewWriter:

package main

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

func main() {
    file, err := os.Create("output_stream.txt")
    if err != nil {
        fmt.Println("Ошибка создания:", err)
        return
    }
    defer file.Close()

    writer := bufio.NewWriter(file)
    for i := 0; i < 10; i++ {
        _, err := writer.WriteString(fmt.Sprintf("Строка %d\n", i+1))
        if err != nil {
            fmt.Println("Ошибка записи:", err)
            return
        }
    }
    writer.Flush() // Важно: сбрасываем буфер
    fmt.Println("Запись завершена")
}

Метод Flush() обязателен — без него данные могут остаться в буфере и не попасть в файл.

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

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

package main

import (
    "fmt"
    "os"
)

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

    // Создать вложенную структуру
    err = os.MkdirAll("a/b/c/d", 0755)
    if err != nil {
        fmt.Println("Ошибка создания вложенных папок:", err)
    } else {
        fmt.Println("Папки созданы")
    }
}

Удаление директории выполняется через os.Remove (только пустая) или os.RemoveAll (рекурсивно, будьте осторожны).

package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.Remove("temp_folder")
    if err != nil {
        fmt.Println("Ошибка удаления:", err)
    }

    // Рекурсивное удаление
    err = os.RemoveAll("a")
    if err != nil {
        fmt.Println("Ошибка рекурсивного удаления:", err)
    } else {
        fmt.Println("Директория a и всё её содержимое удалено")
    }
}

Для обхода содержимого директории используйте os.ReadDir (Go 1.16+) или ioutil.ReadDir (устарел).

package main

import (
    "fmt"
    "os"
)

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

    for _, entry := range entries {
        info, _ := entry.Info()
        if entry.IsDir() {
            fmt.Printf("[DIR]  %s\n", entry.Name())
        } else {
            fmt.Printf("[FILE] %s (размер: %d байт)\n", entry.Name(), info.Size())
        }
    }
}

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

package main

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

func main() {
    root := "."
    err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if info.IsDir() {
            fmt.Printf("Папка: %s\n", path)
        } else {
            fmt.Printf("Файл: %s (%d байт)\n", path, info.Size())
        }
        return nil
    })

    if err != nil {
        fmt.Println("Ошибка обхода:", err)
    }
}

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

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

package main

import (
    "fmt"
    "os"
)

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

func main() {
    path := "example.txt"
    if fileExists(path) {
        info, _ := os.Stat(path)
        fmt.Printf("Файл %s существует\n", path)
        fmt.Printf("Размер: %d байт                

Блоги

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