Поиск подкаталогов в C#

У меня есть список имен файлов, и я хочу искать каталог и все его подкаталоги. Эти каталоги содержат приблизительно 200 000 файлов каждый. Мой код находит файл, но требуется приблизительно 20 минут на файл. Кто-то может предложить лучший метод?

Фрагмент кода

String[] file_names = File.ReadAllLines(@"C:\file.txt");
foreach(string file_name in file_names) 
{
    string[] files = Directory.GetFiles(@"I:\pax\", file_name + ".txt",
                                        SearchOption.AllDirectories);
    foreach(string file in files)
    {
        System.IO.File.Copy(file, 
                            @"C:\" + 
                            textBox1.Text + @"\N\O\" + 
                            file_name + 
                            ".txt"
                            );
    }

}
5
задан George Stocker 17 December 2009 в 20:07
поделиться

7 ответов

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

EDIT: Есть элегантный способ сделать это с LINQ - и менее элегантный способ без него. Вот способ LINQ:

using System;
using System.IO;
using System.Linq;

class Test
{
    static void Main()
    {
        // This creates a lookup from filename to the set of 
        // directories containing that file
        var textFiles = 
            Directory.GetFiles("I:\\pax", "*.txt", SearchOption.AllDirectories)
                     .ToLookup(file => Path.GetFileName(file),
                               file => Path.GetDirectoryName(file));

        string[] fileNames = File.ReadAllLines(@"c:\file.txt");
        // Remove the quotes for your real code :)
        string targetDirectory = "C:\\" + "textBox1.Text" + @"\\N\\O\\";

        foreach (string fileName in fileNames)
        {
            string tmp = fileName + ".txt";
            foreach (string directory in textFiles[tmp])
            {
                string source = Path.Combine(directory, tmp);
                string target = Path.Combine(targetDirectory, tmp);
                File.Copy(source, target);                                       
            }
        }
    }
}

Сообщите мне, если вам нужен способ, отличный от LINQ. Прежде чем я это сделаю, нужно проверить одну вещь - это может копировать несколько файлов поверх друг друга. Это действительно то, чем вы хотите заниматься? (Представьте, что a.txt существует в нескольких местах, а «a» находится в файле.)

13
ответ дан 18 December 2019 в 09:50
поделиться

Вам, наверное, лучше попробовать чтобы загрузить все пути к файлам в память. Вызовите Directory.GetFiles () один раз и поместите результаты в HashSet . Затем выполните поиск в HashSet. Это будет нормально работать, если у вас достаточно памяти. Было бы легко попробовать.

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

Вот код для первого:

String[] file_names = File.ReadAllLines(@"C;\file.txt");
HashSet<string> allFiles = new HashSet<string>();
string[] files = Directory.GetFiles(@"I:\pax\", file_name + ".txt", SearchOption.AllDirectories);
foreach (string file in files)
{
    allFiles.Add(file);
}

foreach(string file_name in file_names)
{
    String file = allFiles.FirstOrDefault(f => f == file_name);
    if (file != null)
    {
        System.IO.File.Copy(file, @"C:\" + textBox1.Text + @"\N\O\" + file_name + ".txt");
    }
}

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

2
ответ дан 18 December 2019 в 09:50
поделиться

Scanning a directory structure is an IO intensive operation, whatever you do, the first GetFiles() call will take the majority of time, by the end of the first call probably most of the file information will be in the file system cache and second call will return in no time when compared to the first call (depending on your free memory and file system cache size).

Probably your best option is turning on indexing on the file system and somehow using it; Querying the Index Programmatically

1
ответ дан 18 December 2019 в 09:50
поделиться

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

Однако для такой проблемы вы можете захотеть подняться на один уровень в формулировке задачи. Если вы часто делаете этот запрос, вы можете создать что-то, что использует FileSystemListener для прослушивания изменений в верхнем каталоге и во всех каталогах ниже него. Заполните его при запуске, просмотрев все каталоги и встроив их в Dictionary <> или HashSet <>. (Да, это та же проблема с памятью, что и решение Linq). Затем, когда вы получите изменения добавления / удаления / переименования файла, обновите словарь. Таким образом, на каждый отдельный запрос можно очень быстро ответить.

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

0
ответ дан 18 December 2019 в 09:50
поделиться

Вы выполняете рекурсивный GetFiles () снова и снова, и это, вероятно, самая дорогая часть.

Попытайтесь загрузить все файлы в память и провести сопоставление с ними самостоятельно.

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

1
ответ дан 18 December 2019 в 09:50
поделиться

На первый взгляд кажется, что существуют .NET API для вызова службы индексирования Windows ... при условии, что на используемом вами компьютере включено индексирование (и я также не уверен, что вышеупомянутое относится к службе индексирования эпохи XP или службе индексирования Windows Search).

Google Search

Одно возможное направление

Другое

0
ответ дан 18 December 2019 в 09:50
поделиться

Попробуйте использовать LINQ для запроса файловой системы. Не уверен на 100% в производительности, но это действительно легко проверить.

var filesResult = from file in new DirectoryInfo(path).GetFiles("*.txt", SearchOption.AllDirectories)
                  where file.Name = filename
                  select file;

Тогда просто делайте все, что хотите, с результатом.

0
ответ дан 18 December 2019 в 09:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: