Создание приложения для поиска файлов на Java

Владимир | | Java.

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

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

В первую очередь, давайте определимся, что именно должен делать наш класс. Для большинства случаев, я думаю, будет достаточно таких возможностей:

  • поиск заданных файлов и папок в начальной папке и всех её вложенных папках;
  • общая статистика поиска (количество найденных файлов и папок, общий размер файлов);
  • использование регулярных выражений в качестве шаблонов для имен найденных файлов и папок (выборочный поиск);
  • поиск отдельно файлов, и отдельно папок.

Теперь посмотрим, какие стандартные библиотеки из JDK (java development kit) нам понадобятся. В состав пакета java.io входит класс File, предназначенный для работы с файлами и папками. Он имеет методы для определения содержимого папки. Так что, нам остаётся добавить просмотр вложенных папок и поддержку регулярных выражений.

Несколько слов о том, что такое регулярные выражения. Это текстовые строки, составленные по определённым правилам, которые можно использовать в качестве шаблонов. Библиотека Java содержит пакет java.util.regex, предназначенный для работы с регулярными выражениями. Подробнее почитать о правилах составления и возможностях регулярных выражений можно в статье: Анализ данных с помощью регулярных выражений или быстрый способ проверки введённых данных.

Таким образом, наш класс, назовём его FileFinder, должен иметь такой набор методов.

Для поиска файлов и папок:
public List findAll(String startPath)
public List findAll(String startPath, String mask)

Для поиска только файлов:
public List findFiles(String startPath)
public List findFiles(String startPath, String mask)

Для поиска только папок:
public List findDirectories(String startPath)
public List findDirectories(String startPath, String mask)

Каждый найденный объект (файл или папка) включается в список результатов только в том случае, если он соответствует регулярному выражению, заданному в параметре mask. Все методы возвращают результат в виде списка объектов типа File.

С помощью методов:
public long getDirectorySize()
public long getFilesNumber()
public long getDirectoriesNumber()

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

Теперь, рассмотрим самую интересную часть. Поиск файлов. Он выполняется с помощью двух методов. Первый,
private List find(String startPath, String mask, int objectType)

выполняет начальную подготовку к поиску: сброс счётчиков, проверку допустимости параметров, компиляцию регулярного выражения и т.п.. Второй метод,
private void search(File topDirectory, List res, int objectType)

вызывается только из метода find(...), он и выполняет поиск. В качестве параметров, методу search(...) передаются: имя папки, указатель на список для хранения найденных объектов, и тип нужного объекта (файлы, папки, всё подряд). С помощью метода listFiles() класса File определяем список файлов и папок в текущей папке (параметр topDirectory), а затем, для каждой найденной папки снова вызываем метод search(...), но в параметре topDirectory передаём найденную папку. Такой способ вызова методов называется рекурсией. Т.е. метод search(...) будет вызывать сам себя до тех пор, пока не пройдёт все вложенные папки. Каждый найденный объект (папка или файл) проверяется на соответствие регулярному выражению (если оно задано) с помощью метода accept(), и, если проверка прошла успешно, добавляется в список результатов.

Теперь посмотрим на весь класс целиком.

package searchtools;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Этот класс предназначен для поиска файлов
 *
 * @author Стаценко Владимир
 * http://www.vova-prog.narod.ru
 */
public class FileFinder {

    //классы для работы с регулярными выражениями
    private Pattern p = null;
    private Matcher m = null;

    //общий размер найденных файлов
    private long totalLength = 0;
    //общее количество найденных файлов
    private long filesNumber = 0;
    //общее количество просмотренных директорий
    private long directoriesNumber = 0;

    //константы для определения объектов, которые нужно найти
    private final int FILES = 0;
    private final int DIRECTORIES = 1;
    private final int ALL = 2;

    /** Создает новые экземпляры FileFinder */
    public FileFinder() {
    }

    /**
     * Этот метод выполняет поиск всех объектов (файлов и директорий),
     * начиная с заданной директории (startPath)
     * @param startPath Начальная директория поиска
     * @return Список (List) найденных объектов
     * @throws java.lang.Exception если возникли ошибки в процессе поиска
     */
    public List findAll(String startPath) throws Exception {
        return find(startPath, "", ALL);
    }

/** * Этот метод выполняет поиск объектов (файлов и директорий), * которые соответствуют заданному регулярному выражению (mask), * начиная с заданной директории (startPath) * @param startPath Начальная директория поиска * @param mask регулярное выражение, которому должны соответствовать * имена найденный объектов * @throws java.lang.Exception если возникли ошибки в процессе поиска * @return Список (List) найденных объектов */ public List findAll(String startPath, String mask) throws Exception { return find(startPath, mask, ALL); } /** * Этот метод выполняет поиск всех файлов, * начиная с заданной директории (startPath) * @param startPath Начальная директория поиска * @return Список (List) найденных объектов * @throws java.lang.Exception если возникли ошибки в процессе поиска */ public List findFiles(String startPath) throws Exception { return find(startPath, "", FILES); } /** * Этот метод выполняет поиск файлов, * которые соответствуют заданному регулярному выражению (mask), * начиная с заданной директории (startPath) * @param startPath Начальная директория поиска * @param mask регулярное выражение, которому должны соответствовать * имена найденный объектов * @throws java.lang.Exception если возникли ошибки в процессе поиска * @return Список (List) найденных объектов */ public List findFiles(String startPath, String mask) throws Exception { return find(startPath, mask, FILES); } /** * Этот метод выполняет поиск всех директорий (папок), * начиная с заданной директории (startPath) * @param startPath Начальная директория поиска * @return Список (List) найденных объектов * @throws java.lang.Exception если возникли ошибки в процессе поиска */ public List findDirectories(String startPath) throws Exception { return find(startPath, "", DIRECTORIES); } /** * Этот метод выполняет поиск директорий (папок), * которые соответствуют заданному регулярному выражению (mask), * начиная с заданной директории (startPath) * @param startPath Начальная директория поиска * @param mask регулярное выражение, которому должны соответствовать * имена найденный объектов * @throws java.lang.Exception если возникли ошибки в процессе поиска * @return Список (List) найденных объектов */ public List findDirectories(String startPath, String mask) throws Exception { return find(startPath, mask, DIRECTORIES); } /** * Возвращает суммарный размер найденных файлов * @return размер найденных файлов (байт) */ public long getDirectorySize() { return totalLength; } /** * Возвращает общее количество найденных файлов * @return количество найденных файлов */ public long getFilesNumber() { return filesNumber; } /** * Возвращает общее количество найденных директорий (папок) * @return количество найденных директорий (папок) */ public long getDirectoriesNumber() { return directoriesNumber; } /* Проверяет, соответствует ли имя файла заданному регулярному выражению. Возвращает true, если найденный объект соответствует регулярному выражению, false - в противном случае. */ private boolean accept(String name) { //если регулярное выражение не задано... if(p == null) { //...значит объект подходит return true; } //создаем Matcher m = p.matcher(name); //выполняем проверку if(m.matches()) { return true; } else { return false; } } /* Этот метод выполняет начальные установки поиска. Затем вызывает метод search для выполнения поиска. */ private List find(String startPath, String mask, int objectType) throws Exception { //проверка параметров if(startPath == null || mask == null) { throw new Exception("Ошибка: не заданы параметры поиска"); } File topDirectory = new File(startPath); if(!topDirectory.exists()) { throw new Exception("Ошибка: указанный путь не существует"); } //если задано регулярное выражение, создаем Pattern if(!mask.equals("")) { p = Pattern.compile(mask, Pattern.CASE_INSENSITIVE | Pattern.UNIcomment_CASE); } //обнуляем все счетчики filesNumber = 0; directoriesNumber = 0; totalLength = 0; //создаем список результатов ArrayList res = new ArrayList(100); //выполняем поиск search(topDirectory, res, objectType); //присваиваем null шаблону, т.к. при следующем вызове find... //регулярное выражение может быть не задано p = null; //возвращаем результат return res; } /* Этот метод выполняет поиск объектов заданного типа. Если, в процессе поиска, встречает вложенную директорию (папку), то рекурсивно вызывает сам себя. Результаты поиска сохраняются в параметре res. Текущая директория - topDirectory. Тип объекта (файл или директория) - objectType. */ private void search(File topDirectory, List res, int objectType) { //получаем список всех объектов в текущей директории File[] list = topDirectory.listFiles(); //просматриваем все объекты по-очереди for(int i = 0; i < list.length; i++) { //если это директория (папка)... if(list[i].isDirectory()) { //...выполняем проверку на соответствие типу объекта // и регулярному выражению... if(objectType != FILES && accept(list[i].getName())) { //...добавляем текущий объект в список результатов, //и обновляем значения счетчиков directoriesNumber++; res.add(list[i]); } //выполняем поиск во вложенных директориях search(list[i], res, objectType); } //если это файл else { //...выполняем проверку на соответствие типу объекта // и регулярному выражению... if(objectType != DIRECTORIES && accept(list[i].getName())) { //...добавляем текущий объект в список результатов, //и обновляем значения счетчиков filesNumber++; totalLength += list[i].length(); res.add(list[i]); } } } } }

Как видите, ничего сложного. Большая часть – это комментарии.

Страница: 1 2

  • Андрей

    Спасибо! Этот класс мне очень помог! Есть одно замечание:
    в методе search
    в строке File[] list = topDirectory.listFiles();
    list может принять значение null (если доступ к папке запрещен) и далее программа генерирует ошибку, поэтому после указанной строки необходимо, судя по всему, вставить проверку на null: что то вроде этого: if (list == null){
    list = new File[]{};
    }

  • Андрей

    Спасибо! Этот класс мне очень помог! Есть одно замечание:
    в методе search
    в строке File[] list = topDirectory.listFiles();
    list может принять значение null (если доступ к папке запрещен) и далее программа генерирует ошибку, поэтому после указанной строки необходимо, судя по всему, вставить проверку на null: что то вроде этого: if (list == null){
    list = new File[]{};
    }

  • Согласен. Действительно, пропустил этот момент.
    Просто тестировал только под Windows и с админовскими правами. Поэтому закрытых папок не было в принципе.
    По-моему, в такой ситуации лучше просто прервать выполнение:
    if (list == null) {
    return;
    }

    Ну и, конечно, можно генерировать исключение (все зависит от требований к программе)
    if (list == null) {
    throw new Exception("....");
    }

  • Согласен. Действительно, пропустил этот момент.
    Просто тестировал только под Windows и с админовскими правами. Поэтому закрытых папок не было в принципе.
    По-моему, в такой ситуации лучше просто прервать выполнение:
    if (list == null) {
    return;
    }

    Ну и, конечно, можно генерировать исключение (все зависит от требований к программе)
    if (list == null) {
    throw new Exception("....");
    }

  • Максим

    Спасибо. Очень хорошо все расписал, приятно читать.

  • Максим

    Спасибо. Очень хорошо все расписал, приятно читать.

  • Тарас

    классная статья,спасибо

  • Тарас

    классная статья,спасибо

  • Alex

    А можно ли было воспользоваться
    private class FindFilenameFilter implements FilenameFilter {
    Pattern p = null;
    Matcher m = null;

    SourceFilenameFilter(String mask) {
    p = Pattern.compile(mask);
    }

    public boolean accept (File f, String s ) {

    m = p.matcher(s);
    if(m.matches()) {
    return true;
    }
    return false;
    }
    }
    и потом в search
    File[] list = topDirectory.listFiles(new FindFilenameFilter(mask));
    ?

    • Наверное можно, если результат подходит 🙂

      Если серьезно, я хотел показать пример использования рекурсии. Естественно, использовать готовую библиотеку проще и удобнее. Просто не для всех случаев существуют эти библиотеки.

  • Alex

    А можно ли было воспользоваться
    private class FindFilenameFilter implements FilenameFilter {
    Pattern p = null;
    Matcher m = null;

    SourceFilenameFilter(String mask) {
    p = Pattern.compile(mask);
    }

    public boolean accept (File f, String s ) {

    m = p.matcher(s);
    if(m.matches()) {
    return true;
    }
    return false;
    }
    }
    и потом в search
    File[] list = topDirectory.listFiles(new FindFilenameFilter(mask));
    ?

    • Наверное можно, если результат подходит 🙂

      Если серьезно, я хотел показать пример использования рекурсии. Естественно, использовать готовую библиотеку проще и удобнее. Просто не для всех случаев существуют эти библиотеки.

  • Сергей

    Почему ошибка при компиляции????
    /***************************************************************************/
    Exception in thread «main» java.lang.ArrayIndexOutOfBoundsException: 1

    at Main.main(Main.java:119)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

    at java.lang.reflect.Method.invoke(Method.java:601)

    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

    • Напишите, пожалуйста, подробнее, как вы запускали программу.

    • Артём

      Вы вышли за пределв массива