Читаем файл из ресурсов в Kotlin: полный гайд с примерами

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

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

Начать курс

Как читать текстовый файл из папки resources в Kotlin: пошаговый туториал

Привет, друг! Если ты когда-нибудь писал на Kotlin под Android или просто создавал консольное приложение, то наверняка сталкивался с простой, но коварной задачей: как прочитать текстовый файл, который лежит в папке resources? Казалось бы, что тут сложного? Но если заглянуть на Stack Overflow, вопрос "How to read a text file from resources in Kotlin?" набирает тысячи просмотров. Значит, проблема реальная.

В этой статье мы разберём 5 надёжных способов сделать это. Ты узнаешь, в чём разница между javaClass и ClassLoader, как не нарваться на NullPointerException и что делать, если твой проект — библиотека, а не приложение. Поехали!

1. Подготовка: куда класть файл?

Прежде чем читать, файл нужно положить в правильное место. В стандартном проекте Kotlin (Gradle, Maven) папка для ресурсов обычно находится здесь:

src/main/resources/

Для тестов — src/test/resources/. Если у тебя Android-проект, ресурсы кладут в src/main/res/raw/ (но там своя логика, об этом ниже). Мы же сосредоточимся на чистом Kotlin/JVM.

Создадим простой файл data.txt с содержимым:

Привет, мир!
Это строки из ресурсов.
Kotlin рулит.

Отлично, инструмент готов. Теперь давай посмотрим, как его открыть.

2. Способ №1: Классика через javaClass

Самый популярный и интуитивно понятный способ — использовать javaClass (это Kotlin-обёртка над getClass() в Java).

import java.io.File

fun readFromResources(fileName: String): String {
    val inputStream = this::class.java.classLoader?.getResourceAsStream(fileName)
        ?: throw IllegalArgumentException("Файл $fileName не найден в ресурсах")
    return inputStream.bufferedReader().readText()
}

Разбор полётов:

  • this::class.java — получаем Java-класс текущего объекта.
  • .classLoader — берём ClassLoader (загрузчик классов).
  • getResourceAsStream(fileName) — открываем поток.
  • Оператор Элвиса ?: бросает исключение, если файла нет.

Вызываем:

fun main() {
    val content = readFromResources("data.txt")
    println(content)
}

На выходе увидим три строки из нашего файла. Просто и элегантно.

3. Способ №2: Используем object для утилит

Если ты пишешь утилитарный класс, удобно вынести логику в object (синглтон). В Kotlin это делается так:

object ResourceReader {
    fun readText(fileName: String): String {
        val stream = ResourceReader::class.java.classLoader
            .getResourceAsStream(fileName)
            ?: throw FileNotFoundException("Ресурс $fileName не найден")
        return stream.bufferedReader().readText()
    }
}

// Использование:
fun main() {
    val text = ResourceReader.readText("config.json")
    println(text)
}

Обрати внимание: мы используем ResourceReader::class.java — это работает, потому что object — это синглтон, у которого есть класс.

4. Способ №3: Читаем как URL (для получения пути)

Иногда нужно не содержимое, а путь к файлу (например, для передачи в другую библиотеку). Тогда используем getResource():

fun getResourcePath(fileName: String): String {
    val url = this::class.java.classLoader.getResource(fileName)
        ?: throw RuntimeException("Ресурс $fileName не найден")
    return url.path
}

Но будь осторожен: если проект запакован в JAR, url.path может указывать внутрь архива, и File не сможет его прочитать. В этом случае лучше всегда использовать поток (getResourceAsStream).

5. Способ №4: Чтение из нескольких файлов (список)

Допустим, у тебя папка resources/data/ с кучей текстовых файлов. Как прочитать все?

import java.io.File
import java.net.URL

fun readAllTextFilesFromFolder(folderName: String): Map<String, String> {
    val classLoader = this::class.java.classLoader
    val resourceUrl = classLoader.getResource(folderName)
        ?: return emptyMap()

    val files = File(resourceUrl.toURI()).listFiles() ?: return emptyMap()

    return files.filter { it.extension == "txt" }.associate { file ->
        file.name to file.readText()
    }
}

Этот метод вернёт Map, где ключ — имя файла, значение — его содержимое. Удобно для конфигов или локализации.

6. Способ №5: Android-специфика (res/raw)

Если ты разрабатываешь под Android, не используй getResourceAsStream — там своя система ресурсов. Вместо этого:

import android.content.Context
import java.io.BufferedReader
import java.io.InputStreamReader

fun readRawTextFile(context: Context, resId: Int): String {
    val inputStream = context.resources.openRawResource(resId)
    val reader = BufferedReader(InputStreamReader(inputStream))
    return reader.readText()
}

Вызов из Activity:

val text = readRawTextFile(this, R.raw.data)

Главное отличие — файл лежит в res/raw/, а идентификатор генерируется автоматически в R.raw.*.

7. Типичные ошибки и как их избежать

Давай разберём три самые частые проблемы, которые валят новичков:

  • NullPointerException — файл не найден. Всегда используй элвис-оператор ?: с осмысленным исключением.
  • FileNotFoundException в JAR — если ты пытаешься создать File из ресурса внутри архива, это не сработает. Используй только поток.
  • Разные ClassLoader'ы — в веб-контейнерах (Tomcat) или тестах иногда нужно использовать Thread.currentThread().contextClassLoader.

Универсальный совет: для продакшена всегда используй getResourceAsStream(), а не getResource() + File.

8. Сравнение способов: что выбрать?

Вот краткая таблица для быстрого выбора:

  • Простое чтение одного файла — способ №1 (javaClass).
  • Утилитарный класс/библиотека — способ №2 (object).
  • Нужен путь, а не содержимое — способ №3, но с о

Блоги

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