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

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

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

Начать курс

Введение

Работа с файловой системой — одна из базовых задач в любом языке программирования. В Kotlin, благодаря его лаконичному синтаксису и мощным расширениям стандартной библиотеки Java, выполнение операций с файлами и директориями становится простым и интуитивно понятным.

В этой статье мы на практике разберем основные операции: создание, чтение, запись, копирование, перемещение и удаление файлов и папок. Все примеры написаны на Kotlin и готовы к использованию в ваших проектах. Материал будет полезен как новичкам, так и опытным разработчикам, которые хотят освежить знания или найти оптимальные решения для повседневных задач.

Основные классы и импорты

Для работы с файловой системой в Kotlin используются классы из пакета java.io (например, File) и java.nio.file (например, Path, Files). Современные рекомендации отдают предпочтение java.nio.file, так как этот API более производительный и удобный.

Для всех примеров нам понадобятся следующие импорты:

import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.nio.file.StandardOpenOption

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

Начнем с самого простого — создания нового файла и папки.

// Создание директории
val dir = File("my_directory")
if (!dir.exists()) {
    dir.mkdirs() // mkdirs() создает все недостающие родительские директории
    println("Директория создана: ${dir.absolutePath}")
}

// Создание файла
val file = File(dir, "example.txt")
if (!file.exists()) {
    file.createNewFile()
    println("Файл создан: ${file.absolutePath}")
}

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

Чтение и запись файлов

Это наиболее частые операции. Kotlin предлагает несколько элегантных способов для работы с содержимым файлов.

Запись в файл

val path = Paths.get("my_directory/write_example.txt")

// Строка для записи
val content = "Привет, мир! Это строка записана в файл.\nВторая строка."

// Простая запись строки (перезаписывает файл)
Files.writeString(path, content)

// Запись списка строк
val lines = listOf("Строка 1", "Строка 2", "Строка 3")
Files.write(path, lines)

// Дозапись в конец файла
val additionalContent = "\nДополнительная строка."
Files.writeString(path, additionalContent, StandardOpenOption.APPEND)

Метод Files.writeString() появился в Java 11 и доступен в Kotlin/JVM. Если вам нужна поддержка более старых версий, используйте Files.write() с указанием кодировки.

Чтение из файла

val path = Paths.get("my_directory/write_example.txt")

// Чтение всего содержимого в строку
val fullContent = Files.readString(path)
println("Содержимое файла:\n$fullContent")

// Чтение в список строк
val allLines: List<String> = Files.readAllLines(path)
allLines.forEachIndexed { index, line ->
    println("Строка ${index + 1}: $line")
}

// Чтение с использованием Stream (для больших файлов)
Files.lines(path).use { stream ->
    stream.forEach { line ->
        println("Обрабатываю строку: $line")
    }
}

Конструкция .use { ... } гарантирует закрытие потока после обработки, что особенно важно при работе с большими файлами.

Копирование, перемещение и переименование

Эти операции также реализуются через java.nio.file.Files.

Копирование файла

val source = Paths.get("my_directory/example.txt")
val destination = Paths.get("my_directory/backup/example_copy.txt")

// Создаем целевую директорию, если ее нет
Files.createDirectories(destination.parent)

// Копирование с заменой существующего файла
Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING)
println("Файл скопирован в: $destination")

Флаг StandardCopyOption.REPLACE_EXISTING указывает, что если файл назначения уже существует, его нужно перезаписать. Без этого флага операция вызовет исключение.

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

Перемещение и переименование в Java NIO — это одна и та же операция Files.move().

val original = Paths.get("my_directory/example.txt")
val renamed = Paths.get("my_directory/renamed_example.txt")

// Переименование (перемещение в той же директории)
Files.move(original, renamed, StandardCopyOption.REPLACE_EXISTING)
println("Файл переименован в: $renamed")

// Перемещение в другую директорию
val moved = Paths.get("my_directory/backup/renamed_example.txt")
Files.createDirectories(moved.parent)
Files.move(renamed, moved, StandardCopyOption.REPLACE_EXISTING)
println("Файл перемещен в: $moved")

Удаление файлов и директорий

Удаление — простая, но важная операция. Будьте осторожны: удаленные данные невозможно восстановить стандартными средствами.

val fileToDelete = Paths.get("my_directory/backup/renamed_example.txt")
val dirToDelete = Paths.get("my_directory/backup")

// Удаление одного файла
if (Files.deleteIfExists(fileToDelete)) {
    println("Файл удален: $fileToDelete")
} else {
    println("Файл не найден: $fileToDelete")
}

// Удаление пустой директории
if (Files.deleteIfExists(dirToDelete)) {
    println("Директория удалена: $dirToDelete")
}

Метод deleteIfExists() безопаснее, чем delete(), так как не выбрасывает исключение, если файл отсутствует. Для удаления непустой директории потребуется рекурсивный обход:

fun deleteRecursively(dir: Path) {
    if (Files.isDirectory(dir)) {
        Files.list(dir).use { stream ->
            stream.forEach { entry ->
                deleteRecursively(entry)
            }
        }
    }
    Files.deleteIfExists(dir)
}

// Использование
val nonEmptyDir = Paths.get("my_directory")
deleteRecursively(nonEmptyDir)                

Блоги

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