Файл большого размера java

Как найти самый большой файл в каталоге при помощи Java

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

Попробуем решить эту задачу, используя лишь два стандартных класса Java — java.io.File и java.util.Arrays

Как мы знаем, директории в файловой системе образуют вложенную структуру, т.е. в одних каталогах могут быть и файлы, и другие каталоги. Во вложенных каталогах также могут быть или файлы, или каталоги и так далее. Напрашивается мысль о том, что наш алгоритм поиска самого большого файла в каталоге должен быть рекурсивным. То есть он примет какой-то основной каталог, где мы ищем файл, и будет его просматривать вглубь, пытаясь найти самый большой файл среди всех остальных. Для того, чтобы нам сделать рекурсивный обход в этом алгоритме эффективным и куда-то «складывать» информацию о самом большом на текущий момент файле (т.е. на момент просмотра алгоритмом каталога, когда мы находимся на определённом уровне вложенности), нам нужна какая-то структура с именем файла и информацией о его размере. Давайте зададим такой класс MaxFileInfo (мы сразу сделаем его статическим и затем встроим в класс основной программы-примера):

 /** * [RU] Класс для хранения информации о самом большом файле * [EN] Class for storing the information about the largest file */ public static class MaxFileInfo < /** * [RU] Хранит размер файла, в байтах * [EN] Stores the file size, in bytes */ private long length; /** * [RU] Имя файла * [EN] The file name */ private String fileName; public long getLength() < return length; >/** * [RU] Возвращает размер файла, в килобайтах * [EN] Returns the file size, in Kilobytes * @return [RU] размер файла (Кб); [EN] file size (Kb) */ public double getLengthKb() < return length / 1024d; >/** * [RU] Возвращает размер файла, в мегабайтах * [EN] Returns the file size, in Megabytes * @return [RU] размер файла (Мб); [EN] file size (Mb) */ public double getLengthMb() < return length / 1048576d; >public void setLength(long length) < this.length = length; >public String getFileName() < return fileName; >public void setFileName(String fileName) < this.fileName = fileName; >>

Как видите, класс содержит лишь два поля для хранения размера файла (в байтах) и названия файла. Также есть методы getLengthKb() и getLengthMb(), вычисляющие размер файла в килобайтах и мегабайтах, соответственно.

Читайте также:  Visual studio 2017 html

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

 public static void getMaxFileInfo(String parentDirectory, MaxFileInfo maxFileInfo) < if (maxFileInfo == null) < throw new IllegalArgumentException("`maxFileInfo` parameter cannot be null!"); >File f = new File(parentDirectory); File[] files = f.listFiles(); if (files == null || files.length == 0) < return; >Arrays.stream(files).forEach(file -> < if (file.isDirectory()) < getMaxFileInfo(file.getPath(), maxFileInfo); >else < long length = file.length(); if (length >maxFileInfo.getLength()) < maxFileInfo.setLength(length); maxFileInfo.setFileName(file.getName()); >> >); >

Давайте разберём алгоритм этого метода. Итак, на входе мы проверяем, что если ссылка на текущий максимальный файл равна null, то это явно ошибка (скорее всего метод был вызван с некорректным значением параметра), поэтому мы сразу выбрасываем исключение IllegalArgumentException:

Затем мы создаём экземпляр стандартного класса File и вызываем следующей строкой метод listFiles() для получения всех директорий и файлов внутри этого каталога. Сразу после этого мы проверяем — не пуст ли этот список, и, если пуст, то возвращаемся из нашего метода, ничего не делая далее:

 File f = new File(parentDirectory); File[] files = f.listFiles(); if (files == null || files.length == 0)

Если же файлы/директории присутствуют, то мы их пробегаем с помощью метода forEach, и для каждого текущего элемента file мы исполняем блок логики в фигурных скобках. Этот блок содержит лишь один оператор if, работающий следующим образом:

  • если текущий элемент — это директория, то вызываем рекурсивно «самих себя», т.е. этот же метод getMaxFileInfo.
  • если же текущий элемент — это файл, то мы получаем его размер в байтах с помощью вызова file.length(), а затем лишь сравниваем с текущей длиной нашего максимального файла, хранящегося по ссылке в maxFileInfo. Если длина текущего элемента (т.е. файла) больше, то мы перезаписываем информацию о длине файла и его имени в ссылке maxFileInfo, вызывая методы setLength и setFileName
Читайте также:  Java заменить значение в map

Вот как это выглядит в коде:

 Arrays.stream(files).forEach(file -> < if (file.isDirectory()) < getMaxFileInfo(file.getPath(), maxFileInfo); >else < long length = file.length(); if (length >maxFileInfo.getLength()) < maxFileInfo.setLength(length); maxFileInfo.setFileName(file.getName()); >> >);

На этом весь алгоритм завершён. С помощью этого кода мы теперь можем рекурсивно обойти любой каталог любого уровня вложенности.

Осталось дело за малым — нам теперь нужно поместить наш статический класс MaxFileInfo, а также метод getMaxFileInfo в какой-то исполняемый класс и написать лишь простую логику вызова нашего рекурсивного метода. Пусть наш файл с примером будет называться MaxFileInfoExample. Вот как будут выглядеть его части и его метод main:

package ru.allineed.samples.files.max_file_info; import ru.allineed.samples.common.OutputUtils; import ru.allineed.samples.config.Localization; import java.io.File; import java.util.Arrays; public class MaxFileInfoExample < public static class MaxFileInfo < // . здесь идёт содержимое класса MaxFileInfo, что рассмотрели выше >public static void getMaxFileInfo(String parentDirectory, MaxFileInfo maxFileInfo) < // . здесь идёт тело метода getMaxFileInfo, что рассмотрели выше >public static void main(String[] args) < OutputUtils.printSampleTitle( "Как найти самый большой файл в каталоге при помощи Java", "How to find the largest file in the directory using Java", "https://allineed.ru/development/java-development/67-java-how-to-find-largest-file"); // [RU] При желании здесь можно поменять путь к каталогу на что-то вида: // String path = "C:\\DIR1\\SUB_DIR2\\SUB_DIR3"; // [EN] If you want you can change the path to the target directory with something like: // String path = "C:\\DIR1\\SUB_DIR2\\SUB_DIR3"; String path = System.getProperty("user.dir"); MaxFileInfo maxFileInfo = new MaxFileInfo(); getMaxFileInfo(path, maxFileInfo); String resultInfo = Localization.getLocalized( String.format("Найден самый большой файл в каталоге '%s': имя = %s, размер = %.2f Мб%n", path, maxFileInfo.getFileName(), maxFileInfo.getLengthMb() ), String.format("The largest file is found in the directory '%s': name = %s, size = %.2f Mb%n", path, maxFileInfo.getFileName(), maxFileInfo.getLengthMb() ) ); System.out.println(resultInfo); >>

Обратите внимание, что в качестве исходного каталога для вызова метода getMaxFileInfo мы задаём переменную path, которая будет равна текущему рабочему каталогу пользователя. Подробнее о стандартных системных свойствах в Java можно прочитать на официальном сайте. Также мы создаём экземпляр класса MaxFileInfo с именем maxFileInfo, которая будет хранить первично нулевой размер максимального файла и пустое имя файла. Оба параметра мы передаём в наш метод getMaxFileInfo:

 String path = System.getProperty("user.dir"); MaxFileInfo maxFileInfo = new MaxFileInfo(); getMaxFileInfo(path, maxFileInfo);

Наконец, после отработки рекурсивного метода, самый большой файл в каталоге будет найден. Остаётся лишь сформировать информационную строку resultInfo с именем и размером файла, после чего вывести её на экран консоли.

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

Делитесь мыслями в комментариях под статьей. Также внизу вы найдете ссылку на этот пример в нашем Git-репозитории и инструкцию по запуску всех примеров на Java, если вы на нашем сайте впервые. Спасибо за внимание и удачи!

Git-репозиторий: https://github.com/AllineedRu/JavaExamples
Примеры из этой статьи: https://github.com/AllineedRu/JavaExamples/blob/main/allineed-core/src/main/java/ru/allineed/samples/files/max_file_info/MaxFileInfoExample.java

Как запускать примеры из статей? | Права на использованные иконки

Источник

Построчное чтение больших файлов на Java

В предыдущей статье Как сохранить текстовый файл на Java мы научились записывать текстовый файл больших размеров. А в этот раз давайте научимся его читать построчно и каждую строку как-то обрабатывать. Например, будем искать наибольшую длину строки. Поскольку размер файла больше 100 МБ, его чтение происходит с небольшой задержкой.

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

Чтение файла целиком

В пакете nio есть удобный статичный метод Files.readAllLines(). Он возвращает список строк, т.е. считывает содержимое файла полностью в память.

public int getMaxLineLengthAllLines(File file) <
try <
return Files.readAllLines(Path.of(file.toURI()))
.stream()
.mapToInt(String::length)
.max()
.orElse( 0 );
> catch (Exception e) <
throw new RuntimeException(e);
>
>

Из этого списка мы получаем стрим, затем преобразуем его в список длин строк с помощью метода mapToInt() и находим среди значений наибольшее. Для кейсов, когда список пустой, максимум не может быть найден, поэтому возвращаем по умолчанию 0. Для удобства, в блоке catch оборачиваем исключение в RuntimeException, чтобы не прописывать исключения в сигнатуре нашего метода.

Как вы уже догадываетесь, читать большой файл в память не самая лучшая затея. Особенно если вы обрабатываете каждую строку отдельно. Поэтому чтение файла размером в 111 МБ на моём ноуте занимает порядка 1100 миллисекунд, т.е. чуть больше секунды.

Чтение файла через стрим

Рассмотрим более эффективную реализацию, где мы обойдёмся без создания промежуточной коллекции со всеми строками, а сразу будем получать стрим и по очереди обрабатывать строки из него. Воспользуемся методом Files.lines(). Этот вариант более предпочтителен с точки зрения экономии памяти.

public int getMaxLineLengthStream(File file) <
try ( var lines = Files.lines(Paths.get(file.toURI()), StandardCharsets. UTF_8 )) <
return lines.mapToInt(String::length)
.max()
.orElse( 0 );
> catch (Exception e) <
throw new RuntimeException(e);
>
>

Обратите внимание, что стрим мы инициализируем в блоке try-with-resources, чтобы Java автоматически освобождала ресурсы при выходе из метода. В остальном реализация этого метода похожа на предыдущую: получаем список длин строк и ищем среди них максимальную.

Данная реализация с тем же файлом работает почти в 2 раза быстрее, примерно за 600 миллисекунд.

Буферизация

Если отказаться от пакета nio и воспользоваться классической связкой FileReader, обёрнутый в BufferedReader, то этот вариант даст ещё больший прирост производительности.

public int getMaxLineLengthBuffered(File file) <
try (
var fr = new FileReader(file);
var br = new BufferedReader(fr)
) <
var line = br.readLine();
var maxLineLength = 0 ;

while (line != null ) if (maxLineLength < line.length()) maxLineLength = line.length();
>
line = br.readLine();
>
return maxLineLength;
> catch (Exception e) throw new RuntimeException(e);
>
>

Ридеры инициализируем в начале блока try-with-resources, заводим необходимые переменные и в цикле, строка за строкой, считываем весь файл. Попутно проверяем длину текущей строки, и если она больше той, что мы нашли ранее, то выбираем её в качестве результата. После обработки всего файла возвращаем найденное значение.

Данная реализация обрабатывает файл целиком примерно за 530 миллисекунд. Но кода здесь писать чуть больше.

Выводы

Вариант решения задачи с помощью Files.readAllLines() работает наименее эффективно, т.к. считывает все строки в память. Этот вариант имеет смысл использовать только если чтения всего файла целиком не избежать. Если же есть возможность обрабатывать файлы построчно, то лучше использовать Files.lines(), который сразу возвращает стрим.

А наибольшей эффективностью обладает классическая связка FileReader + BufferedReader, но она является блокирующей, в отличие от первых двух вариантов.

Источник

Обработка огромных файлов с помощью Java

Недавно мне пришлось обработать набор файлов, содержащих исторические данные о рынке тик-тика, и быстро понял, что ни один из них не может быть считан в память с использованием традиционного InputStream поскольку каждый файл имеет размер более 4 гигабайт. Emacs не мог даже открыть их.

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

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

Окей, давайте посмотрим на эти файлы и извлечем некоторые данные. Похоже, они содержат текстовые строки ASCII с полями, разделенными запятыми.

Пример: EUR/USD,20120102 00:01:30.420,1.29451,1.2949

Справедливо, я мог бы написать программу для этого формата. Но чтение и анализ файлов – это ортогональные понятия; так что давайте сделаем шаг назад и подумаем об общем дизайне, который можно использовать повторно в случае, если в будущем мы столкнемся с подобной проблемой.

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

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

Итак, сначала интерфейс Decoder . Идея состоит в том, чтобы постепенно декодировать объекты из MappedByteBuffer или возвращать нуль, если в буфере не осталось объектов.

Источник

Оцените статью