Avatar ·

Как прочитать все файлы из папки в Java? (рекурсивно и нет)

Всем привет! Я только начал изучать Java и столкнулся с такой задачей: мне нужно написать программу, которая читает все файлы из определённой директории. Например, у меня есть папка "data" с текстовыми файлами разного размера, и я хочу получить их содержимое в виде строк. Пробовал сделать сам, но пока не очень получается. Вот что я написал:
import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;

public class ReadFiles {
    public static void main(String[] args) {
        File folder = new File("data");
        File[] listOfFiles = folder.listFiles();
        
        for (File file : listOfFiles) {
            if (file.isFile()) {
                BufferedReader reader = new BufferedReader(new FileReader(file));
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
                reader.close();
            }
        }
    }
}
Но код выдаёт ошибку "NullPointerException" на строке `for (File file : listOfFiles)`. Что я делаю не так? Как правильно получить список файлов? И ещё: внутри папки могут быть подпапки, как мне обработать их рекурсивно? Буду благодарен за подробное объяснение с рабочими примерами. <<>>

Основная проблема: NullPointerException

Ошибка возникает, потому что метод listFiles() возвращает null, если переданная директория не существует или возникла ошибка ввода-вывода. В вашем случае папка "data" не найдена (например, не та рабочая директория). Всегда проверяйте результат на null:

File folder = new File("data");
File[] listOfFiles = folder.listFiles();

if (listOfFiles != null) {
    for (File file : listOfFiles) {
        // обработка
    }
} else {
    System.out.println("Папка не найдена: " + folder.getAbsolutePath());
}

Правильный способ: Java 7+ (NIO.2)

Начиная с Java 7, рекомендуется использовать пакет java.nio.file. Он более надёжный и производительный. Вот два основных подхода:

1. Чтение всех файлов (не рекурсивно)

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

public class ReadFilesNIO {
    public static void main(String[] args) throws IOException {
        Path dir = Paths.get("data");
        
        // Используем try-with-resources для автоматического закрытия потока
        try (Stream<Path> paths = Files.list(dir)) {
            paths
                .filter(Files::isRegularFile) // только файлы, не папки
                .forEach(path -> {
                    try {
                        String content = Files.readString(path);
                        System.out.println("Файл: " + path.getFileName());
                        System.out.println(content);
                    } catch (IOException e) {
                        System.err.println("Ошибка чтения: " + path);
                    }
                });
        }
    }
}

2. Рекурсивный обход (включая подпапки)

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

public class ReadFilesRecursive {
    public static void main(String[] args) throws IOException {
        Path startDir = Paths.get("data");
        
        // Files.walk() обходит всё дерево рекурсивно
        try (Stream<Path> paths = Files.walk(startDir)) {
            paths
                .filter(Files::isRegularFile)
                .forEach(path -> {
                    try {
                        // Читаем файл как строку (Java 11+)
                        String content = Files.readString(path);
                        System.out.println("Файл: " + path);
                        System.out.println(content);
                        System.out.println("---");
                    } catch (IOException e) {
                        System.err.println("Ошибка: " + path);
                    }
                });
        }
    }
}

Важные моменты:

  • Files.list() — только файлы и папки первого уровня (не рекурсивно).
  • Files.walk() — рекурсивный обход всех поддиректорий.
  • Files.readString() доступен с Java 11. Для более старых версий используйте Files.readAllLines() или new String(Files.readAllBytes(path)).
  • try-with-resources автоматически закрывает поток, чтобы избежать утечек.
  • Всегда обрабатывайте IOException — файлы могут быть недоступны.

Пример с фильтрацией по расширению

try (Stream<Path> paths = Files.walk(Paths.get("data"))) {
    paths
        .filter(Files::isRegularFile)
        .filter(path -> path.toString().endsWith(".txt"))
        .forEach(path -> {
            // обработка только .txt файлов
        });
}

Если вам нужно читать файлы построчно (например, для больших файлов), используйте Files.lines() — он возвращает Stream<String>.

<<>>

Альтернативный подход: классический File + рекурсия

Если вы по какой-то причине не можете использовать NIO (например, очень старая версия Java), вот решение на основе java.io.File с рекурсивным обходом:

import java.io.*;

public class ReadFilesLegacy {
    public static void main(String[] args) {
        File root = new File("data");
        processDirectory(root);
    }
    
    private static void processDirectory(File dir) {
        File[] files = dir.listFiles();
        if (files == null) return; // защита от ошибок
        
        for (File file : files) {
            if (file.isDirectory()) {
                processDirectory(file); // рекурсия для подпапок
            } else if (file.isFile()) {
                readFile(file);
            }
        }
    }
    
    private static void readFile(File file) {
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.err.println("Ошибка: " + file.getAbsolutePath());
        }
    }
}

Дополнительные советы:

  • Используйте File.listFiles(FileFilter) для фильтрации на уровне ОС (например, только .txt файлы).
  • Для больших объёмов данных переходите на NIO — он работает быстрее и надёжнее.
  • Не забывайте про кодировку: new FileReader(file, StandardCharsets.UTF_8).

Этот вариант полностью решает вашу задачу, но в современной Java предпочтительнее использовать java.nio.file.