Рубрики
Без рубрики

Чтение файла Строка за строкой на Java

Автор оригинала: Vasyl Lagutin.

В информатике файл – это ресурс, используемый для дискретной записи данных в запоминающее устройство компьютера. В Java ресурс обычно представляет собой объект, реализующий автоклавируемый интерфейс.

Чтение файлов и ресурсов имеет множество применений:

  • Статистика, аналитика и отчеты
  • Машинное Обучение
  • Работа с большими текстовыми файлами или журналами

Иногда эти файлы могут быть абсурдно большими, с хранящимися гигабайтами или терабайтами, и полное чтение их неэффективно.

Возможность читать файл строка за строкой дает нам возможность искать только соответствующую информацию и прекращать поиск, как только мы найдем то, что ищем. Это также позволяет нам разбивать данные на логические части, например, если файл был отформатирован в формате CSV.

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

Сканер

Один из самых простых способов чтения файла строка за строкой в Java может быть реализован с помощью класса Scanner . Сканер разбивает свой ввод на маркеры, используя шаблон разделителя, которым в нашем случае является символ новой строки:

Scanner scanner = new Scanner(new File("filename"));
while (scanner.hasNextLine()) {
   String line = scanner.nextLine();
   // process the line
}

Метод hasNextLine() возвращает true , если на входе этого сканера есть другая строка, но сам сканер в этот момент не проходит мимо каких-либо входных данных или не считывает какие-либо данные.

Чтобы прочитать строку и двигаться дальше, мы должны использовать метод nextLine () . Этот метод продвигает сканер дальше текущей строки и возвращает ввод, который не был достигнут изначально. Этот метод возвращает остальную часть текущей строки, исключая любой разделитель строк в конце строки. Затем позиция чтения устанавливается в начало следующей строки, которая будет прочитана и возвращена при повторном вызове метода.

Поскольку этот метод продолжает поиск по входным данным в поисках разделителя строк, он может буферизировать все входные данные при поиске конца строки, если разделители строк отсутствуют.

Буферизованное считывающее устройство

Класс BufferedReader представляет собой эффективный способ считывания символов, массивов и строк из потока ввода символов.

Как описано в именовании, этот класс использует буфер. Объем буферизованных данных по умолчанию составляет 8192 байта, но по соображениям производительности он может быть установлен на пользовательский размер:

BufferedReader br = new BufferedReader(new FileReader(file), bufferSize);

Файл или, скорее, экземпляр класса File не является подходящим источником данных для BufferedReader , поэтому нам нужно использовать FileReader , который расширяет InputStreamReader . Это удобный класс для чтения информации из текстовых файлов, и он не обязательно подходит для чтения необработанного потока байтов:

try (BufferedReader br = new BufferedReader(new FileReader(file))) {
    String line;
    while ((line = br.readLine()) != null) {
       // process the line
    }
}

Инициализация буферизованного считывателя была написана с использованием синтаксиса try-with-resources , специфичного для Java 7 или выше. Если вы используете более старую версию, вам следует инициализировать переменную br перед оператором try и закрыть ее в блоке finally .

Вот пример предыдущего кода без синтаксиса try-with-resources:

BufferedReader br = new BufferedReader(new FileReader(file));
try {
    String line;
    while ((line = br.readLine()) != null) {
       // process the line
    }
} finally {
    br.close();
}

Код будет проходить по строкам предоставленного файла и остановится, когда встретится со строкой null , которая является концом файла.

Не путайтесь, так как null не равно пустой строке, и файл будет прочитан до конца.

Метод линий

Класс BufferedReader также имеет метод строк , который возвращает Поток . Этот поток содержит строки , которые были прочитаны BufferedReader в качестве его элементов.

Вы можете легко преобразовать этот поток в список, если вам нужно:

List list = new ArrayList<>();

try (BufferedReader br = new BufferedReader(new FileReader(file))) {
    list = br.lines().collect(Collectors.toList());    
}

Чтение этого списка аналогично чтению в потоке, которые рассматриваются в следующем разделе:

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

list.forEach(System.out::println);

Потоки Java 8

Если вы уже знакомы с Java 8 Streams , вы можете использовать их в качестве более чистой альтернативы устаревшему циклу:

try (Stream stream = Files.lines(Paths.get(fileName))) {
    stream.forEach(System.out::println);
}

Здесь мы снова используем синтаксис try-with-resources , инициализируя поток строк с помощью Files.lines() статического вспомогательного метода. Ссылка на метод System.out::println используется для демонстрационных целей, и вам следует заменить ее любым кодом, который вы будете использовать для обработки своих строк текста.

В дополнение к чистому API потоки очень полезны, когда вы хотите применить несколько операций к данным или отфильтровать что-то.

Давайте предположим, что у нас есть задача распечатать все строки, которые находятся в данном текстовом файле и заканчиваются символом”/”. Строки должны быть преобразованы в верхний регистр и отсортированы в алфавитном порядке.

Изменив наш первоначальный пример “API потоков”, мы получим очень чистую реализацию:

try (Stream stream = Files.lines(Paths.get(fileName))) {
    stream
        .filter(s -> s.endswith("/"))
        .sorted()
        .map(String::toUpperCase)
        .forEach(System.out::println);
}

Метод filter() возвращает поток, состоящий из элементов этого потока, соответствующих данному предикату. В нашем случае мы оставляем только те, которые заканчиваются на “/”.

Метод map() возвращает поток, состоящий из результатов применения данной функции к элементам этого потока.

Метод toUpperCase() класса String помогает нам достичь желаемого результата и используется здесь в качестве ссылки на метод, как и вызов println из нашего предыдущего примера.

Метод sorted() возвращает поток, состоящий из элементов этого потока, отсортированных в соответствии с естественным порядком. Вы также можете предоставить пользовательский Компаратор , и в этом случае сортировка будет выполняться в соответствии с ним.

Хотя порядок операций может быть изменен для методов filter() , sorted () и map () , forEach() всегда следует размещать в конце, поскольку это терминальная операция. Он возвращает пустоту и, если на то пошло, ничто не может быть приковано к нему дальше.

Apache Commons

Если вы уже используете Apache Commons в своем проекте, вы можете использовать помощника, который считывает все строки из файла в Список<Строка> :

List lines = FileUtils.readLines(file, "UTF-8");
for (String line: lines) {
    // process the line
}

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

Вывод

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