Мы находимся в состоянии войны с Россией с 24 февраля 2022 года. Чтобы помочь Украине как можно скорее добиться победы, пожалуйста, игнорируйте всю российскую продукцию, компании, проекты... Всё.
Также вы можете помочь Вооруженным Силам Украины здесь: https://bank.gov.ua/ru/news/all/natsionalniy-bank-vidkriv-spetsrahunok-dlya-zboru-koshtiv-na-potrebi-armiyi
Мы освободим нашу Родину от русских захватчиков и спасем Европу от агрессивного бесчеловечного российского режима. Я обещаю.
Если вы хотите поддержать меня лично, это можно сделать здесь: https://ko-fi.com/VladPVS
Многопоточная библиотека .NET, предоставляющая возможность быстрого поиска файлов или каталогов по различным критериям поиска.
Версия .NET Core доступна здесь.
Инструмент поиска файлов MIF основан на этой библиотеке. Вы можете попробовать, если хотите оценить скорость работы прямо сейчас.
using FastSearchLibrary;
4.5.1
, если вы используете библиотеку v1.1.6.1, или 4.6.2
, если вы используете хотя бы версию v1.1.7.2: Проект -> Свойства -> Целевая платформа. Следующие классы предоставляют функции поиска:
FileSearcher
и DirectorySearcher
содержат статические методы, позволяющие выполнять поиск по разным критериям. Эти методы возвращают результат только после полного завершения выполнения.string folder
- начать каталог поискаstring pattern
— строка поиска, которая сопоставляется с именами файлов в пути. Этот параметр может содержать комбинацию допустимого пути и подстановочных знаков (* и ?), но не поддерживает регулярные выражения.Примеры:
List < FileInfo > files = FileSearcher . GetFiles ( @"C:Users" , " *.txt " ) ;
Находит все файлы *.txt
в C:Users
используя метод одного потока.
List < FileInfo > files = FileSearcher . GetFilesFast ( @"C:Users" , " *SomePattern*.txt " ) ;
Находит все файлы, соответствующие соответствующему шаблону, используя несколько потоков в пуле потоков.
Task < List < FileInfo > > task = FileSearcher . GetFilesFastAsync ( @"C:" , " a?.txt " ) ;
Находит все файлы, соответствующие соответствующему шаблону, используя несколько потоков в пуле потоков в качестве асинхронной операции.
string folder
- начать каталог поискаFunc<FileInfo, bool> isValid
— делегат, определяющий алгоритм выбора файла.Примеры:
Task < List < FileInfo > > task = FileSearcher . GetFilesFastAsync ( @"D:" , ( f ) =>
{
return ( f . Name . Contains ( " Pattern " ) || f . Name . Contains ( " Pattern2 " ) ) && f . LastAccessTime >= new DateTime ( 2018 , 3 , 1 ) && f . Length > 1073741824 ;
} ) ;
Находит все файлы, соответствующие соответствующим условиям, используя несколько потоков в пуле потоков в качестве асинхронной операции.
Вы также можете использовать регулярные выражения:
Task < List < FileInfo > > task = FileSearcher . GetFilesFastAsync ( @"D:" , ( f ) =>
{
return Regex . IsMatch ( f . Name , @".*Imagine[s_-]Dragons.*.mp3$" ) ;
} ) ;
Находит все файлы, соответствующие соответствующему регулярному выражению, используя несколько потоков в пуле потоков в качестве асинхронной операции.
Если вы хотите выполнить сложный поиск с получением результатов в реальном времени, вам следует использовать экземпляр класса FileSearcher
, который имеет различные перегрузки конструктора. Класс FileSearcher
включает в себя следующие события:
event EventHandler<FileEventArgs> FilesFound
— срабатывает при обнаружении следующей порции файлов. Событие включает в себя List<FileInfo> Files { get; }
Свойство, содержащее список найденных файлов.event EventHandler<SearchCompleted> SearchCompleted
— срабатывает, когда процесс поиска завершен или остановлен. Событие включает bool IsCanceled { get; }
Свойство, содержащее значение, определяющее, остановился ли процесс поиска при вызове метода StopSearch()
. Чтобы получить возможность остановки процесса поиска, необходимо использовать конструктор, принимающий параметр CancellationTokenSource.Пример:
class Searcher
{
private static object locker = new object ( ) ; // locker object
private FileSearcher searcher ;
private List < FileInfo > files ;
public Searcher ( )
{
files = new List < FileInfo > ( ) ; // create list that will contain search result
}
public void StartSearch ( )
{
CancellationTokenSource tokenSource = new CancellationTokenSource ( ) ;
// create tokenSource to get stop search process possibility
searcher = new FileSearcher ( @"C:" , ( f ) =>
{
return Regex . IsMatch ( f . Name , @".*[iI]magine[s_-][dD]ragons.*.mp3$" ) ;
} , tokenSource ) ; // give tokenSource in constructor
searcher . FilesFound += ( sender , arg ) => // subscribe on FilesFound event
{
lock ( locker ) // using a lock is obligatory
{
arg . Files . ForEach ( ( f ) =>
{
files . Add ( f ) ; // add the next part of the received files to the results list
Console . WriteLine ( $" File location: { f . FullName } , n Creation.Time: { f . CreationTime } " ) ;
} ) ;
if ( files . Count >= 10 ) // one can choose any stopping condition
searcher . StopSearch ( ) ;
}
} ;
searcher . SearchCompleted += ( sender , arg ) => // subscribe on SearchCompleted event
{
if ( arg . IsCanceled ) // check whether StopSearch() called
Console . WriteLine ( " Search stopped. " ) ;
else
Console . WriteLine ( " Search completed. " ) ;
Console . WriteLine ( $" Quantity of files: { files . Count } " ) ; // show amount of finding files
} ;
searcher . StartSearchAsync ( ) ;
// start search process as an asynchronous operation that doesn't block the called thread
}
}
Обратите внимание, что все обработчики событий FilesFound
не являются потокобезопасными, поэтому, чтобы предотвратить потерю результата, следует использовать ключевое слово lock
, как вы можете видеть в примере выше, или использовать потокобезопасную коллекцию из пространства имен System.Collections.Concurrent
.
Есть 2 дополнительных параметра, которые можно установить. Это handlerOption
и suppressOperationCanceledException
. Параметр ExecuteHandlers handlerOption
представляет собой экземпляр перечисления ExecuteHandlers
, который указывает, где выполняются обработчики событий FilesFound:
InCurrentTask
означает, что обработчики событий FileFound
будут выполняться в той задаче, где были найдены файлы.InNewTask
означает, что обработчики событий FilesFound
будут выполняться в новой задаче. Значение по умолчанию — InCurrentTask
. В большинстве случаев это более предпочтительно. Значение InNewTask
следует использовать только в том случае, если обработчики выполняют очень сложную работу, занимающую много времени, например, анализ каждого найденного файла. Параметр bool suppressOperationCanceledException
определяет, необходимо ли подавлять OperationCanceledException. Если параметр suppressOperationCanceledException
имеет значение false
и вызывается метод StopSearch(), будет выброшено исключение OperationCanceledException
. В этом случае вам придется обработать исключение вручную. Если параметр suppressOperationCanceledException
имеет значение true
и вызывается метод StopSearch(), исключение OperationCanceledException
обрабатывается автоматически, и вам не нужно его перехватывать. Значение по умолчанию — true
.
Пример:
CancellationTokenSource tokenSource = new CancellationTokenSource ( ) ;
FileSearcher searcher = new FileSearcher ( @"D:Program Files" , ( f ) =>
{
return Regex . IsMatch ( f . Name , @".{1,5}[Ss]ome[Pp]attern.txt$" ) && ( f . Length >= 8192 ) ; // 8192b == 8Kb
} , tokenSource , ExecuteHandlers . InNewTask , true ) ; // suppressOperationCanceledException == true
Классы FileSearcher
и DirectorySearcher
могут осуществлять поиск только в одном каталоге (и, конечно же, во всех подкаталогах), но что, если вы хотите выполнять поиск в нескольких каталогах одновременно?
Конечно, вы можете создать несколько экземпляров класса FileSearcher
(или DirectorySearcher
) и запускать их одновременно, но события FilesFound
(или DirectoriesFound
) будут происходить для каждого созданного вами экземпляра. Как правило, это неудобно. Классы FileSearcherMultiple
и DirectorySearcherMultiple
призваны решить эту проблему. Они похожи на FileSearcher
и DirectorySearcher
, но могут выполнять поиск в нескольких каталогах. Разница между FileSearcher
и FileSearcherMultiple
заключается в том, что конструктор класса Multiple
принимает список каталогов вместо одного каталога.
Пример:
List < string > folders = new List < string >
{
@"C:UsersPublic" ,
@"C:WindowsSystem32" ,
@"D:Program Files" ,
@"D:Program Files (x86)"
} ; // list of search directories
List < string > keywords = new List < string > { " word1 " , " word2 " , " word3 " } ; // list of search keywords
FileSearcherMultiple multipleSearcher = new FileSearcherMultiple ( folders , ( f ) =>
{
if ( f . CreationTime >= new DateTime ( 2015 , 3 , 15 ) &&
( f . Extension == " .cs " || f . Extension == " .sln " ) )
{
foreach ( var keyword in keywords )
if ( f . Name . Contains ( keyword ) )
return true ;
}
return false ;
} , tokenSource , ExecuteHandlers . InCurrentTask , true ) ;
Настоятельно рекомендуется использовать ключевое слово «await» при использовании любого асинхронного метода. Это позволяет получить возможные исключения из метода для последующей обработки, что показано в следующем примере кода. Обработка ошибок в предыдущих примерах была пропущена для простоты.
Пример:
using System ;
using System . Collections . Generic ;
using System . Diagnostics ;
using System . IO ;
using System . Text . RegularExpressions ;
using System . Threading ;
using FastSearchLibrary ;
namespace SearchWithAwait
{
class Program
{
private static object locker = new object ( ) ;
private static List < FileInfo > files ;
private static Stopwatch stopWatch ;
static void Main ( string [ ] args )
{
string searchPattern = @".mp4$" ;
StartSearch ( searchPattern ) ;
Console . ReadKey ( true ) ;
}
private static async void StartSearch ( string pattern )
{
stopWatch = new Stopwatch ( ) ;
stopWatch . Start ( ) ;
Console . WriteLine ( " Search has been started. n " ) ;
files = new List < FileInfo > ( ) ;
List < string > searchDirectories = new List < string >
{
@"C:" ,
@"D:"
} ;
FileSearcherMultiple searcher = new FileSearcherMultiple ( searchDirectories , ( f ) =>
{
return Regex . IsMatch ( f . Name , pattern ) ;
} , new CancellationTokenSource ( ) ) ;
searcher . FilesFound += Searcher_FilesFound ;
searcher . SearchCompleted += Searcher_SearchCompleted ;
try
{
await searcher . StartSearchAsync ( ) ;
}
catch ( AggregateException ex )
{
Console . WriteLine ( $" Error occurred: { ex . InnerException . Message } " ) ;
}
catch ( Exception ex )
{
Console . WriteLine ( $" Error occurred: { ex . Message } " ) ;
}
finally
{
Console . Write ( " n Press any key to continue... " ) ;
}
}
private static void Searcher_FilesFound ( object sender , FileEventArgs arg )
{
lock ( locker ) // using of the lock is mandatory
{
arg . Files . ForEach ( ( f ) =>
{
files . Add ( f ) ; // add the next part of the received files to the results list
Console . WriteLine ( $" File location: { f . FullName } n Creation.Time: { f . CreationTime } n " ) ;
} ) ;
}
}
private static void Searcher_SearchCompleted ( object sender , SearchCompletedEventArgs arg )
{
stopWatch . Stop ( ) ;
if ( arg . IsCanceled ) // check whether StopSearch() called
Console . WriteLine ( " Search stopped. " ) ;
else
Console . WriteLine ( " Search completed. " ) ;
Console . WriteLine ( $" Quantity of files: { files . Count } " ) ; // show amount of finding files
Console . WriteLine ( $" Spent time: { stopWatch . Elapsed . Minutes } min { stopWatch . Elapsed . Seconds } s { stopWatch . Elapsed . Milliseconds } ms " ) ;
}
}
}
Существует ограничение Windows на полное имя файла в 260 символов. В большинстве случаев библиотека игнорирует такие «длинные» пути. Но если вы хотите обойти это ограничение, вам следует выполнить следующие шаги:
<Project name>
в обозревателе решений, щелкните правой кнопкой мыши -> Add
-> New item
-> Application manifest file
. Затем добавьте содержимое этого файла в манифест перед последним закрытым тегом.HKLMSYSTEMCurrentControlSetControlFileSystem
Затем создайте параметр LongPathsEnabled
(тип REG_DWORD) со значением 1
. Это зависит от производительности вашего компьютера, текущей загрузки, но обычно Fast
методы и метод экземпляра StartSearch()
выполняются как минимум в 2 раза быстрее, чем простой однопоточный рекурсивный алгоритм.