|
| 1 | +# FastSearchLibrary |
| 2 | +The multithreading .NET library that provides opportunity to fast find files or directories using different search criteria. |
| 3 | +#### Works really fast. Check it yourself! |
| 4 | + |
| 5 | +## INSTALLATION |
| 6 | +1. Download archive with last [release](https://github.com/VladPVS/FastSearchLibrary/releases "Last release") |
| 7 | +2. Extract content from some directoty. |
| 8 | +3. Copy files .dll and .xml files in directory of your project. |
| 9 | +4. Add library to your project: Solution Explorer -> Reference -> item AddReference in contex menu -> Browse |
| 10 | +5. Add appropriate namespace: `using FastSearchLibrary;` |
| 11 | +6. Set target .NET Framework version as 4.5.1 or higher: Project -> <YourProjectName> Properties -> Target framework |
| 12 | + |
| 13 | +## CONTENT |
| 14 | + |
| 15 | +Next classes provide search functionality: |
| 16 | +* FileSearcher |
| 17 | +* DirectorySearcher |
| 18 | +* FileSearcherMultiple |
| 19 | +* DirectorySearcherMultiple |
| 20 | + |
| 21 | +## USE PRINCIPLES |
| 22 | +### Basic opportunities |
| 23 | + * Classes `FilesSearcher` and `DirectorySearcher` contain static method that allow execute search by different criteria. |
| 24 | + These methods return result only when they fully complete execution. |
| 25 | + * Methods that have "Fast" ending divide task on several |
| 26 | + subtasks that executes simultaneously in thread pool. |
| 27 | + * Methods that have "Async" ending return Task and don't block the called thread. |
| 28 | + * First group of methods accept 2 parameters: |
| 29 | + * `string folder` - start search directory |
| 30 | + * `string pattern` - the search string to match against the names of files in path. |
| 31 | + This parameter can contain a combination of valid literal path and wildcard (* and ?) |
| 32 | + characters, but doesn't support regular expressions. |
| 33 | + |
| 34 | + Examples: |
| 35 | + |
| 36 | + List<FileInfo> files = FileSearcher.GetFiles(@"C:\Users", "*.txt"); |
| 37 | + Finds all `*.txt` files in `C:\Users` using one thread method. |
| 38 | + |
| 39 | + List<FileInfo> files = FileSearcher.GetFilesFast(@"C:\Users", "*SomePattern*.txt"); |
| 40 | + Finds all files that match appropriate pattern using several thread in thread pool. |
| 41 | + |
| 42 | + Task<List<FileInfo>> task = FileSearcher.GetFilesFastAsync(@"C:\", "a?.txt"); |
| 43 | + Finds all files that match appropriate pattern using several thread in thread pool as |
| 44 | + an asynchronous operation. |
| 45 | + |
| 46 | + * Second group of methods accept 2 parameters: |
| 47 | + * `string folder` - start search directory |
| 48 | + * `Func<FileInfo, bool> isValid` - delegate that determines algorithm of file selection. |
| 49 | + |
| 50 | + Examples: |
| 51 | + |
| 52 | + Task<List<FileInfo>> task = FileSearcher.GetFilesFastAsync(@"D:\", (f) => |
| 53 | + { |
| 54 | + return (f.Name.Contains("Pattern") || f.Name.Contains("Pattern2")) && |
| 55 | + f.LastAccessTime >= new DateTime(2018, 3, 1) && f.Length > 1073741824; |
| 56 | + }); |
| 57 | + Finds all files that match appropriate conditions using several thread in thread pool as |
| 58 | + an asynchronous operation. |
| 59 | + |
| 60 | + You also can use regular expressions: |
| 61 | + |
| 62 | + Task<List<FileInfo>> task = FileSearcher.GetFilesFastAsync(@"D:\", (f) => |
| 63 | + { |
| 64 | + return (f) => Regex.IsMatch(f.Name, @".*Imagine[\s_-]Dragons.*.mp3$"); |
| 65 | + }); |
| 66 | + |
| 67 | + Finds all files that match appropriate regular expression using several thread in thread pool as |
| 68 | + an asynchronous operation. |
| 69 | + |
| 70 | + ### Advanced opportunities |
| 71 | + If you want to execute some complicated search with realtime result getting you should use instance of `FileSearcher` class, |
| 72 | + that has various constructor overloads. |
| 73 | + `FileSearcher` class includes next events: |
| 74 | + * `event EventHandler<FileEventArgs> FilesFound` - fires when next portion of files is found. |
| 75 | + Event includes `List<FileInfo> Files { get; }` property that contains list of finding files. |
| 76 | + * `event EventHandler<SearchCompleted> SearchCompleted` - fires when search process is completed or stopped. |
| 77 | + Event includes `bool IsCanceled { get; }` property that contains value that defines whether search procees stopped by calling |
| 78 | + `StopSearch()` method. |
| 79 | + To stop search process possibility one have to use constructor that accept CancellationTokenSource parameter. |
| 80 | + |
| 81 | + Example: |
| 82 | + |
| 83 | + class Searcher |
| 84 | + { |
| 85 | + private static object locker = new object(); // locker object |
| 86 | + |
| 87 | + private FileSearcher searcher; |
| 88 | + |
| 89 | + List<FileInfo> files; |
| 90 | + |
| 91 | + public Searcher() |
| 92 | + { |
| 93 | + files = new List<FileInfo>(); // create list that will contain search result |
| 94 | + } |
| 95 | + |
| 96 | + public void Startsearch() |
| 97 | + { |
| 98 | + CancellationTokenSource tokenSource = new CancellationTokenSource(); |
| 99 | + // create tokenSource to get stop search process possibility |
| 100 | + |
| 101 | + searcher = new FileSearcher(@"C:\", (f) => |
| 102 | + { |
| 103 | + return Regex.IsMatch(f.Name, @".*[iI]magine[\s_-][dD]ragons.*.mp3$"); |
| 104 | + }, tokenSource); // give tokenSource in constructor |
| 105 | + |
| 106 | + |
| 107 | + searcher.FilesFound += (sender, arg) => // subscribe on FilesFound event |
| 108 | + { |
| 109 | + lock (locker) // using a lock is obligatorily |
| 110 | + { |
| 111 | + arg.Files.ForEach((f) => |
| 112 | + { |
| 113 | + files.Add(f); // add the next part of the received files to the results list |
| 114 | + Console.WriteLine($"File location: {f.FullName}, \nCreation.Time: {f.CreationTime}"); |
| 115 | + }); |
| 116 | + |
| 117 | + if (files.Count >= 10) // one can choose any stopping condition |
| 118 | + searcher.StopSearch(); |
| 119 | + } |
| 120 | + }; |
| 121 | + |
| 122 | + searcher.SearchCompleted += (sender, arg) => // subscribe on SearchCompleted event |
| 123 | + { |
| 124 | + if (arg.IsCanceled) // check whether StopSearch() called |
| 125 | + Console.WriteLine("Search stopped."); |
| 126 | + else |
| 127 | + Console.WriteLine("Search completed."); |
| 128 | + |
| 129 | + Console.WriteLine($"Quantity of files: {files.Count}"); // show amount of finding files |
| 130 | + }; |
| 131 | + |
| 132 | + searcher.StartSearchAsync(); |
| 133 | + // start search process as an asynchronous operation that doesn't block the called thread |
| 134 | + } |
| 135 | + } |
| 136 | + Note that all `FilesFound` event handlers are not thread safe so to prevent result loosing one should use |
| 137 | + `lock` keyword as you can see in example above or use thread safe collection from `System.Collections.Concurrent` namespace. |
| 138 | + |
| 139 | + ### Extended opportunities |
| 140 | + FileSearcher class includes 2 additional parameters: `handlerOption`, `suppressOperationCanceledException`. |
| 141 | + `ExecuteHandlers handlerOption` parameter represents instance of `ExecuteHandlers` enumeration that specifies where |
| 142 | + FilesFound event handlers are executed: |
| 143 | + * `InCurrentTask` value means that `FileFound` event handlers will be executed in that task where files were found. |
| 144 | + * `InNewTask` value means that `FilesFound` event handlers will be executed in new task. |
| 145 | + Default value is `InCurrentTask`. It is more preferably in most cases. `InNewTask` value one should use only if handlers execute |
| 146 | + very sophisticated work that takes a lot of time, e.g. parsing of each found file. |
| 147 | + |
| 148 | + `bool suppressOperationCanceledException` parameter determines whether necessary to suppress |
| 149 | + OperationCanceledException. |
| 150 | + If `suppressOperationCanceledException` parameter has value `false` and StopSearch() method calls the `OperationCanceledException` |
| 151 | + will be thrown. In this case you have to process the exception manually. |
| 152 | + If `suppressOperationCanceledException` parameter has value `true` and StopSearch() method calls the `OperationCanceledException` |
| 153 | + is processed automatically and you don't need to catch it. |
| 154 | + Default value is `true`. |
| 155 | + |
| 156 | + Example: |
| 157 | + |
| 158 | + CancellationTokenSource tokenSource = new CancellationTokenSource(); |
| 159 | + |
| 160 | + FileSearcher searcher = new FileSearcher(@"D:\Program Files", (f) => |
| 161 | + { |
| 162 | + return Regex.IsMatch(f.Name, @".{1,5}[Ss]ome[Pp]attern.txt$") && (f.Length >= 8192); // 8192b == 8Kb |
| 163 | + }, tokenSource, ExecuteHandlers.InNewTask, true); // suppressOperationCanceledException == true |
| 164 | + |
| 165 | + ### MULTIPLE SEARCH |
| 166 | + `FileSearcher` and `DirectorySearcher` classes can search only in one directory (and in all subdirectories surely) |
| 167 | + but what if you want to perform search in several directories at same time? |
| 168 | + Of course, you can create some instances of `FileSearcher` (or `DirectorySearcher`) class and launch them simultaneously, |
| 169 | + but `FilesFound` (or `DirectoriesFound`) events will occur for each instance you create. As a rule, it's inconveniently. |
| 170 | + Classes `FileSearcherMultiple` and `DirectorySearcherMultiple` are intended to solve this problem. |
| 171 | + They are similar to `FileSearcher` and `DirectorySearcher` but can execute search in several directories. |
| 172 | + The difference between `FileSearcher` and `FileSearcheMultiple` is that constructor of `Multiple` class accepts list of |
| 173 | + directories instead one directory. |
| 174 | + |
| 175 | + Example: |
| 176 | + |
| 177 | + List<string> folders = new List<string> |
| 178 | + { |
| 179 | + @"C:\Users\Public", |
| 180 | + @"C:\Windows\System32", |
| 181 | + @"D:\Program Files", |
| 182 | + @"D:\Program Files (x86)" |
| 183 | + }; // list of search directories |
| 184 | + |
| 185 | + List<string> keywords = new List<string> { "word1", "word2", "word3" }; // list of search keywords |
| 186 | + |
| 187 | + FileSearcherMultiple multipleSearcher = new FileSearcherMultiple(folders, (f) => |
| 188 | + { |
| 189 | + if (f.CreationTime >= new DateTime(2015, 3, 15) && |
| 190 | + (f.Extension == ".cs" || f.Extension == ".sln")) |
| 191 | + foreach (var keyword in keywords) |
| 192 | + if (f.Name.Contains(keyword)) |
| 193 | + return true; |
| 194 | + return false; |
| 195 | + }, tokenSource, ExecuteHandlers.InCurrentTask, true); |
| 196 | + |
| 197 | +### SPEED OF WORK |
| 198 | +It depends on your computer performance, current loading, but usually `Fast` methods and instance method `StartSearch()` are |
| 199 | +performed at least in 2 times faster then simple one-thread recursive algorithm if you use modern multicore processor of course. |
| 200 | + |
| 201 | + |
0 commit comments