Почему работа с файлами в 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