Как читать и писать файлы в Go: пошаговая инструкция
Стандартная библиотека Go покрывает 90 % задач по работе с файлами без сторонних зависимостей. Разберём чтение, запись и типичные ловушки — от простого os.ReadFile до построчного сканирования больших логов.

Что понадобится до начала
Все примеры проверены на Go 1.22. Подойдёт любая версия от 1.16 и выше — именно тогда в пакете os появились функции ReadFile и WriteFile, которые заменили устаревший ioutil.
- Установленный Go (проверьте командой
go versionв терминале). - Любой текстовый редактор или IDE с поддержкой Go — VS Code с расширением Go, GoLand, даже Vim.
- Каталог для экспериментов: создайте папку
filelabи выполните внутриgo mod init filelab.
В конце вы будете уметь: прочитать файл целиком, прочитать его построчно, записать данные с нуля и дописать в конец. На всё уйдёт около 20 минут.
Шаг 1. Читаем файл целиком
Самый короткий путь — os.ReadFile. Функция возвращает срез байт и ошибку. Подходит для файлов, которые помещаются в оперативную память: конфиги, небольшие JSON, шаблоны.
package main
import (
"fmt"
"log"
"os"
)
func main() {
// Читаем весь файл в память одним вызовом
data, err := os.ReadFile("example.txt")
if err != nil {
log.Fatalf("не удалось прочитать файл: %v", err)
}
fmt.Println(string(data))
}
Проверка: создайте рядом файл example.txt с любым текстом и запустите go run main.go. В терминале появится содержимое файла.
Важный момент: os.ReadFile загружает весь файл в память. Для лог-файла на несколько гигабайт это не подходит — перейдите к шагу 2.
Шаг 2. Читаем файл построчно
Когда файл большой или вам нужна обработка по строкам, используйте bufio.Scanner. Он читает данные порциями и не загружает весь файл разом.
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
file, err := os.Open("access.log")
if err != nil {
log.Fatalf("не удалось открыть файл: %v", err)
}
defer file.Close() // закрываем файл при выходе из функции
scanner := bufio.NewScanner(file)
lineNum := 0
for scanner.Scan() {
lineNum++
fmt.Printf("%d: %s\n", lineNum, scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatalf("ошибка при чтении: %v", err)
}
}
Проверка: создайте access.log с несколькими строками, запустите программу — увидите пронумерованные строки.
Ловушка с длинными строками
По умолчанию буфер сканера — 64 КБ на строку*. Если строка длиннее, scanner.Err() вернёт bufio.ErrTooLong. Решение — увеличить буфер:
// Устанавливаем максимальный размер строки в 1 МБ
scanner.Buffer(make([]byte, 0, 1024*1024), 1024*1024)
Вызывайте Buffer до первого Scan(), иначе паники не будет, но буфер не применится.
Шаг 3. Записываем файл с нуля
Зеркальная функция — os.WriteFile. Принимает путь, данные и права доступа.
package main
import (
"log"
"os"
)
func main() {
content := []byte("Первая строка\nВторая строка\n")
// 0644 — владелец читает и пишет, остальные только читают
err := os.WriteFile("output.txt", content, 0644)
if err != nil {
log.Fatalf("не удалось записать файл: %v", err)
}
}
Проверка: после запуска в каталоге появится output.txt. Откройте его — внутри две строки.
Если файл с таким именем уже существует, os.WriteFile перезапишет его без предупреждения. Это самая частая причина потери данных у начинающих.
Шаг 4. Дописываем в конец файла
Для добавления данных без перезаписи нужен os.OpenFile с флагами.
package main
import (
"log"
"os"
)
func main() {
// O_APPEND — писать в конец, O_CREATE — создать, если нет, O_WRONLY — только запись
file, err := os.OpenFile("output.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("не удалось открыть файл: %v", err)
}
defer file.Close()
_, err = file.WriteString("Третья строка, дописанная позже\n")
if err != nil {
log.Fatalf("не удалось дописать: %v", err)
}
}
Проверка: запустите после шага 3 — в output.txt теперь три строки.
Шаг 5. Буферизованная запись для большого объёма
Если вы генерируете тысячи строк — логи, CSV, дампы — каждый вызов WriteString отправляет системный вызов. Это медленно. Оберните запись в bufio.Writer:
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
file, err := os.Create("big_output.csv")
if err != nil {
log.Fatalf("не удалось создать файл: %v", err)
}
defer file.Close()
writer := bufio.NewWriter(file)
for i := 0; i < 100_000; i++ {
// Данные копятся в буфере, а не уходят в ядро на каждой итерации
fmt.Fprintf(writer, "%d,value_%d\n", i, i)
}
// Сбрасываем оставшееся из буфера на диск
if err := writer.Flush(); err != nil {
log.Fatalf("ошибка при flush: %v", err)
}
}
Проверка: после запуска файл big_output.csv содержит 100 000 строк. Без bufio.Writer тот же цикл работает заметно медленнее — разница видна уже на сотнях тысяч строк.
Правило:defer file.Close()не гарантирует, что данные из буфераbufio.Writerдоедут до диска. Всегда вызывайтеFlush()явно перед закрытием файла — иначе последний кусок данных может потеряться без единой ошибки.
Сводная таблица: когда что использовать
| Задача | Функция / подход | Когда подходит | Когда не подходит |
|---|---|---|---|
| Прочитать файл целиком | os.ReadFile | Конфиги, шаблоны, JSON до нескольких МБ | Файлы крупнее доступной RAM |
| Прочитать построчно | bufio.Scanner | Логи, CSV, любые файлы произвольного размера | Бинарные файлы без разделителя строк |
| Записать с нуля | os.WriteFile | Однократная запись небольшого объёма | Нужно дописывать, а не перезаписывать |
| Дописать в конец | os.OpenFile + O_APPEND | Логирование, накопление данных | Нужна запись в середину файла |
| Массовая генерация | bufio.Writer | Десятки тысяч записей и выше | Одна-две строки — накладные расходы не оправдаются |
Если не получилось
Пять ошибок, которые встречаются чаще всего:
open example.txt: no such file or directory— файл не найден. Проверьте, что запускаете программу из того же каталога, где лежит файл. В Go путь относителен к рабочей директории процесса, а не к расположению.go-файла.open output.txt: permission denied— нет прав на запись. На Linux/macOS проверьте права каталога командойls -la. На Windows убедитесь, что файл не открыт другой программой.- Файл пуст после записи через
bufio.Writer— забыли вызватьFlush(). Данные остались в буфере и не дошли до диска. bufio.Scanner: token too long— строка длиннее буфера по умолчанию (64 КБ). Увеличьте буфер методомBuffer, как показано в шаге 2.- Файл перезаписан вместо дополнения — использовали
os.WriteFileилиos.Createвместоos.OpenFileс флагомos.O_APPEND. Эти функции усекают файл до нуля перед записью.
Что делать дальше
Вы освоили базовые операции. Следующие шаги зависят от задачи:
- Работа с JSON-файлами — пакет
encoding/jsonи связкаjson.NewDecoderсos.Openпозволяет декодировать JSON потоково, не загружая весь файл. - Временные файлы — функция
os.CreateTempсоздаёт файл с уникальным именем в системной директории для временных данных. Полезно для промежуточных результатов. - Работа с путями — пакет
filepathкорректно склеивает пути для разных ОС, что избавляет от ручной возни со слешами и обратными слешами.
Документация стандартной библиотеки по пакетам os, bufio и io — основной справочник. Там описаны все флаги, интерфейсы и граничные случаи, которые не поместились в эту инструкцию.
* Буфер сканера — область памяти, куда bufio.Scanner считывает данные из файла порциями. Размер по умолчанию — 64 КБ (65 536 байт). Если одна строка длиннее этого лимита, сканер прекращает работу с ошибкой, а не молча обрезает строку.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: Пакет os — стандартная библиотека Go, Пакет bufio — стандартная библиотека Go
Частые вопросы
Пакет ioutil удалён? Можно ли его использовать?
Как прочитать бинарный файл — картинку или архив?
defer file.Close() — это достаточно для безопасного закрытия?
Что если у меня Go версии 1.15 или ниже?
Как обработать файл с кодировкой Windows-1251, а не UTF-8?
Программа не видит файл, хотя он точно лежит рядом с main.go. Что не так?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.