Как эффективно проверить, существуют ли файлы с подходящим именем файла (регулярное выражение или подстановочный знак) в каталоге? - PullRequest
2 голосов
/ 01 ноября 2011

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

Примеры использования подстановочных знаков:

  • ????. *
  • ???????. *
  • *. Png
  • *. Jpg

Примеры использования регулярных выражений:

  • [012] {4}. *
  • [012] {7}. *

Проблема в том, что каталог, который я должен проверить, содержит до 500.000 файлов.Единственный известный мне способ выполнить такие тесты - это использовать методы класса File :

String[] list()
String[] list(FilenameFilter filter)
File[] listFiles()
File[] listFiles(FileFilter filter)
File[] listFiles(FilenameFilter filter)

Проблема в том, что в основном все они реализованы одинаково: сначала вызовlist () для получения всех доступных файлов и применения к ним фильтра.

Представьте себе, что произойдет, если мы захотим применить это к папке, содержащей 500.000 файлов ...

Если есть какая-либо альтернатива в Java для полученияимя файла первого соответствующего файла, относящегося к файлам в каталоге без нумерации их всех?

Если JNI - единственный вариант - есть ли библиотека, которая может сделать это с помощью предварительно скомпилированных двоичных файлов дляшесть основных платформ (Linux, Windows и OSX, каждая 32 и 64-битная)?

Ответы [ 3 ]

3 голосов
/ 02 ноября 2011

Я думаю, что вы в замешательстве. Насколько я знаю, ни одна текущая ОС не поддерживает листинг / поиск шаблонов в интерфейсе своей файловой системы. Все утилиты, которые поддерживают шаблоны, делают это, перечисляя каталог (например, используя readdir() в системах POSIX), а затем выполняя сопоставление строк.

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

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

  • извлечение списка файлов один раз и его кэширование, возможно, в сочетании с интерфейсом уведомления о событиях файловой системы для обновлений (например, JNotify или интерфейс Java 7 WatchService ).

EDIT:

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

Если вы можете использовать относительно недавнюю версию Java, вы можете взглянуть на классы Java NIO ( 1 , 2 ), которые не похоже, чтобы иметь ту же слабость.

1 голос
/ 02 ноября 2011

это займет около 1 минуты на моей машине (которая устарела)

import java.io.*;
import java.util.*;
import java.util.regex.*;
public class Main {
    static void match(File dir, Pattern pattern, List<File> matching) {
        File[] files = dir.listFiles();
        if(files==null) {
            System.out.println(dir + " is strange!");
            return;
        }
        for (File file : files)
            if (file.isDirectory()) match(file, pattern, matching);
            else if (file.isFile()) {
                Matcher matcher = pattern.matcher(file.getName());
                if (matcher.matches()) {
                    matching.add(file);
                    //System.out.println(file + "************");
                }
            }
    }
    static void makeFiles(File dir,int n) throws IOException {
        for(int i=0;i<n;i++) {
            File file=new File(dir,i+".foo");
            FileWriter fw=new FileWriter(file);
            fw.write(1);
            fw.close();
        }
    }
    public static void main(String[] args) throws IOException {
        File dir = new File("data");
        final int n=500000;
        //makeFiles(dir,n);
        long t0=System.currentTimeMillis();
        Pattern pattern = Pattern.compile(".*\\.foo");
        List<File> matching = new LinkedList<File>();
        match(dir, pattern, matching);
        long t1=System.currentTimeMillis();
        System.out.println("found: "+matching.size());
        System.out.println("elapsed time: "+(t1-t0)/1000.);
        System.out.println("files/second: "+n/((t1-t0)/1000.));
    }
}
0 голосов
/ 01 ноября 2011

Я думаю, вы ставите телегу с пословицей перед лошадью.

  1. Как сказал Кнут, преждевременная оптимизация - корень всего зла. Вы пытались использовать метод FileFilter и обнаружили, что он слишком медленный для приложения?

  2. Почему у вас так много файлов в одной папке? Возможно, более выгодным подходом было бы разделить эти файлы каким-либо образом, вместо того чтобы хранить их все в одной папке.

...