Avatar ·

Чтение текстового файла в Java: пошаговое руководство для начинающих

Всем привет! Я учу Java около месяца и столкнулся с задачей, которая кажется простой, но вызывает у меня ступор. Нужно прочитать обычный текстовый файл (например, log.txt или data.txt) и вывести его содержимое в консоль. Я перепробовал кучу вариантов из интернета, но каждый раз что-то идёт не так. Вот что я пробовал:
import java.io.*;

public class ReadFile {
    public static void main(String[] args) {
        File file = new File("data.txt");
        Scanner scanner = new Scanner(file);
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            System.out.println(line);
        }
        scanner.close();
    }
}
Этот код выдаёт ошибку "FileNotFoundException", хотя файл лежит в той же папке, что и исходник. Потом я нашёл пример с BufferedReader и FileReader, но там нужно обрабатывать исключения, и я запутался в try-catch. Ещё видел, что в Java 8 можно использовать Files.lines(), но это вообще магия какая-то. Подскажите, пожалуйста, как правильно и безопасно читать текстовые файлы в Java? Мне нужно понять: 1) Как указать путь к файлу, чтобы он точно нашёлся? 2) Как правильно закрывать ресурсы, чтобы не было утечек? 3) Какой способ самый надёжный для начинающего? Буду благодарен за подробное объяснение с примерами кода, которые можно скопировать и запустить. P.S. Если это важно — файл небольшой, около 100 строк, кодировка UTF-8. <<>>

Классический способ: BufferedReader + FileReader (Java 7+)

Самый надёжный и понятный метод для начинающих — использовать BufferedReader в связке с FileReader. Главное преимущество — вы читаете файл построчно, а не целиком, что экономит память.

import java.io.*;

public class ReadFileExample {
    public static void main(String[] args) {
        // Укажите полный путь или относительный (от корня проекта)
        String filePath = "data.txt"; 
        
        // try-with-resources автоматически закроет файл
        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.err.println("Ошибка чтения файла: " + e.getMessage());
        }
    }
}

Почему это работает?

  • FileReader открывает файл для чтения символов (с учётом кодировки по умолчанию).
  • BufferedReader оборачивает FileReader и добавляет буферизацию — читает данные большими блоками, а не по одному символу.
  • Конструкция try-with-resources (появилась в Java 7) гарантирует, что ресурс (BufferedReader) будет закрыт автоматически, даже если возникнет исключение.

Как указать путь к файлу?

Есть три варианта:

  • Относительный путь — от корня проекта (папки, где лежит src). Пример: "data.txt" или "resources/data.txt".
  • Абсолютный путь — полный путь на диске: "C:/Users/User/project/data.txt" (для Windows) или "/home/user/project/data.txt" (для Linux/Mac).
  • Через ClassLoader — если файл лежит внутри JAR или в classpath: getClass().getClassLoader().getResourceAsStream("data.txt").

Совет: всегда проверяйте, существует ли файл, с помощью new File("путь").exists() перед чтением.

Учёт кодировки (UTF-8)

Если ваш файл в UTF-8, используйте InputStreamReader вместо FileReader:

try (BufferedReader br = new BufferedReader(
        new InputStreamReader(new FileInputStream(filePath), "UTF-8"))) {
    // чтение такое же
}

Что делать с FileNotFoundException?

Ошибка возникает, если файл не найден. Проверьте:

  • Текущую рабочую директорию (выведите System.getProperty("user.dir")).
  • Расширение файла (например, .txt).
  • Права доступа к файлу.

В примере выше исключение перехватывается блоком catch, и программа не падает.

Альтернатива для Java 8+: Files.lines()

Если вы используете Java 8 или новее, есть более лаконичный способ через Stream API:

import java.nio.file.*;
import java.util.stream.*;

public class ReadFileStream {
    public static void main(String[] args) throws IOException {
        // Files.lines() возвращает Stream строк
        try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {
            lines.forEach(System.out::println);
        }
    }
}

Этот метод тоже использует try-with-resources и автоматически закрывает файл. Он идеален, если нужно обработать строки (например, отфильтровать или преобразовать).

Итог: что выбрать новичку?

Начните с BufferedReader + FileReader — это классика, которая работает везде и легко понимается. Когда освоитесь, переходите к Files.lines() для более современного стиля.

<<>>

Простой способ для маленьких файлов: Files.readAllLines()

Если ваш файл небольшой (до нескольких мегабайт) и вы хотите получить все строки сразу в виде списка, используйте статический метод Files.readAllLines() из пакета java.nio.file. Это самый короткий и понятный вариант для начинающих.

import java.nio.file.*;
import java.util.*;
import java.io.IOException;

public class SimpleRead {
    public static void main(String[] args) {
        try {
            List<String> lines = Files.readAllLines(Paths.get("data.txt"));
            for (String line : lines) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.err.println("Ошибка: " + e.getMessage());
        }
    }
}

Плюсы и минусы

  • Плюсы: минимальный код, не нужно заботиться о буферизации или закрытии ресурсов — метод делает всё сам. Поддерживает кодировку UTF-8 по умолчанию.
  • Минусы: файл загружается в память целиком. Для больших файлов (сотни мегабайт) это может вызвать проблемы с памятью.

Когда использовать?

Этот способ идеален для конфигурационных файлов, логов небольшого размера или учебных примеров. Если вы только учитесь — начните с него, а потом переходите к более продвинутым методам.

Обратите внимание: метод readAllLines возвращает List<String>, поэтому вы можете легко обращаться к любой строке по индексу, например lines.get(0) для первой строки.