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

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

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

Начать курс

Почему работа с файлами в Kotlin — это не больно

Представьте: вы пишете приложение, которое должно сохранять настройки пользователя, логировать ошибки или обрабатывать CSV-файлы с тысячами строк. В Java за это отвечают десятки классов BufferedReader, FileWriter и бесконечные try-catch. А что в Kotlin? Язык, созданный JetBrains, не просто «упрощает» Java — он переосмысливает работу с данными. Здесь файлы открываются одной строкой, а ошибки обрабатываются элегантно.

В этой статье мы разберём работу с файлами и файловой системой в Kotlin от базовых операций до продвинутых техник. Вы узнаете, как читать и записывать файлы, управлять директориями, работать с JSON и CSV, а также избегать типичных ошибок. Готовы? Погнали!

Основы: как Kotlin упрощает Java IO

Главная фишка Kotlin — extension-функции. Вместо того чтобы писать десятки строк для чтения файла, вы используете одну функцию .readText() или .readLines(). Давайте посмотрим на конкретные примеры.

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

Самый простой способ прочитать текстовый файл — использовать readText(). Этот метод возвращает строку с содержимым файла. Важно: файл автоматически закроется после чтения.

import java.io.File

fun main() {
    val content = File("example.txt").readText()
    println(content)
}

Если файл большой, лучше читать построчно:

val lines = File("example.txt").readLines()
lines.forEach { line -> println(line) }

А если нужно читать потоком (например, для обработки больших логов):

File("example.txt").useLines { lines ->
    lines.forEach { line ->
        // Обработка каждой строки
        println(line)
    }
}

Обратите внимание на useLines() — это функция, которая гарантирует закрытие ресурса после завершения блока. Выглядит магически, но работает как try-with-resources в Java, только короче.

Запись в файл

Запись данных в Kotlin тоже интуитивна. Вот три варианта:

// 1. Перезаписать файл
File("output.txt").writeText("Привет, мир!")

// 2. Дописать в конец файла
File("output.txt").appendText("Новая строка\n")

// 3. Записать список строк
val list = listOf("строка1", "строка2", "строка3")
File("output.txt").writeText(list.joinToString("\n"))

Кстати, если вы работаете с бинарными данными (изображения, архивы), используйте writeBytes() и readBytes():

val imageBytes = File("image.png").readBytes()
// Обработка байтов...
File("copy.png").writeBytes(imageBytes)

Управление файловой системой: создание, удаление, перемещение

Kotlin не изобретает велосипед, но делает работу с файловой системой удобнее. Все операции доступны через класс java.io.File с расширениями.

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

Чтобы создать директорию (и все родительские, если их нет):

val dir = File("data/logs/2024")
val created = dir.mkdirs() // Возвращает true, если создано
println("Директория создана: $created")

Обратите внимание: mkdir() создаёт только последнюю папку, а mkdirs() — всю цепочку.

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

val file = File("temp.txt")
if (file.exists()) {
    if (file.delete()) {
        println("Файл удалён")
    } else {
        println("Не удалось удалить")
    }
}

// Рекурсивное удаление директории
File("data").deleteRecursively()

Будьте осторожны с deleteRecursively() — это необратимая операция. Проверяйте дважды, что вы удаляете нужные данные.

Перемещение и копирование

val source = File("old.txt")
val target = File("new_dir/new.txt")

// Перемещение (переименование)
source.renameTo(target)

// Копирование
source.copyTo(target, overwrite = true)

Параметр overwrite = true позволяет перезаписать существующий файл. Без него вы получите исключение, если файл уже существует.

Продвинутые техники: работа с JSON, CSV и настройками

Теперь перейдём к реальным сценариям, которые встречаются в каждом проекте.

Сериализация в JSON

Для работы с JSON в Kotlin чаще всего используют библиотеку kotlinx.serialization. Добавьте зависимость в build.gradle.kts:

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
}

Пример сериализации и десериализации:

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import java.io.File

@Serializable
data class User(val name: String, val age: Int)

fun main() {
    val user = User("Анна", 28)
    
    // Запись в файл
    val json = Json.encodeToString(user)
    File("user.json").writeText(json)
    
    // Чтение из файла
    val loadedJson = File("user.json").readText()
    val loadedUser = Json.decodeFromString<User>(loadedJson)
    println(loadedUser) // User(name=Анна, age=28)
}

Красота в том, что вся работа с JSON укладывается в 3-4 строки. Никаких геттеров/сеттеров — просто data class и аннотация.

Парсинг CSV-файлов

CSV — формат, который любят дата-сайентисты. В Kotlin нет встроенной библиотеки для CSV, но можно написать свой парсер за 10 минут или использовать kotlin-csv.

Пример ручного парсинга:

File("data.csv").useLines { lines ->
    lines.drop(1) // Пропускаем заголовок
        .map { it.split(",") }
        .forEach { row ->
            val name = row[0]
            val age = row[1].toInt()
            println("$name: $age лет")
        }
}

Для сложных CSV (с кавычками, экранированием) лучше взять готовую библиотеку — она обработает все edge cases.

Работа с конфигурационными файлами

Часто нужно хранить настройки приложения. Простой способ — использовать .properties файлы:

import java.util.Properties

fun saveSettings() {
    val props = Properties().apply {
        setProperty("theme", "dark")
        setProperty("language", "ru")
    }
    props.store(File("config.properties").writer(), "Настройки приложения")
}

fun loadSettings(): Properties                

Блоги

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