[Closed] Directory.EnumerateFiles method
I’m looking for solutions how to collect all files in specified folder (considering all subfolders)
using multiple file-extensions search pattern. For this I decide to use net Directory.GetFiles method but find out that Directory.EnumerateFiles provide fester method.
How to transform below function to use Enumerate files method.
Example with StringCollection.contains method
fn collectFiles folder: extensions: =
(
local files = #()
local sioPath = dotNetClass "System.IO.Path"
local sioDir = dotNetClass "System.IO.Directory"
local sc = dotnetobject "System.Collections.Specialized.StringCollection"
local sioSOpt = (dotNetClass "System.IO.SearchOption").AllDirectories
sc.AddRange extensions
local allFiles = (sioDir.GetFiles folder "*.*" sioSOpt)
for f in allFiles where sc.contains ((dotNetObject "System.String" (sioPath.GetExtension f)).toLower()) do append files f
files
)
collectFiles folder:@"c: emp" extensions:#(".jpeg",".xml",".zip", ".exe", ".msi", ".psd")
Example with regex.isMatch method
fn collectFiles folder: extPattern: =
(
local files = #()
local sioDir = dotNetClass "System.IO.Directory"
local rx = dotNetClass "System.Text.RegularExpressions.Regex"
local rxo = (dotNetClass "System.Text.RegularExpressions.RegexOptions").IgnoreCase
local sioSOpt = (dotNetClass "System.IO.SearchOption").AllDirectories
local allFiles = (sioDir.GetFiles folder "*.*" sioSOpt)
for str in allFiles where rx.isMatch str extPattern rxo do append files str
files
)
collectFiles folder:@"c: emp" extPattern:@"\.(gif|jpe?g|tif?f|png|psd|msi|zip|txt|xml)$"
The only advantage of enumeration in this case, is that if you are searching for a particular file, you can stop enumerating once you find it, whereas with Directory.GetFiles, you must wait for the entire operation to finish before you have access to the files. When called from Maxscript, even this will not be available, as there is no native language support for the iterator pattern.
Thanks lo. I assumed that but if we collect matched paths in searching process maybe we will get faster result. After all probably in max there in not differens.
What do you think which method is better 1st (with StringCollections) or 2nd (regex) because
I plan to add more extensions?
Why do you believe that the dotnet methods are faster than the native maxscript methods?
This is about 2.5 times faster than your first code snippet.
fn collectFiles2 folder: extensions: =
(
local sioDir = dotNetClass "System.IO.Directory"
local sioSOpt = (dotNetClass "System.IO.SearchOption").AllDirectories
local allFiles = sioDir.GetFiles folder "*.*" sioSOpt
for f in allFiles where findItem extensions (tolower (getfilenametype f)) > 0 collect f
)
I believe that a dynamically compiled c# assembly will be faster, because you will not have to convert the result of GetFiles to a maxscript string array, which I believe is the main bottleneck other than the directory traversal.
I agree with you.I not know how to create an assembly in VS and for this easy task your method ei. combination of .net & mxs works great. Cheers!
edit: I know what’s going on but I don’t know about “asDotNetObject:on” property. This is very useful info. Thaks again
I’d like to explain what’s really going on in this line in your first code and why it’s slow:
for f in allFiles where sc.contains ((dotNetObject "System.String" (sioPath.GetExtension f)).toLower()) do append files f
let’s start with the f I marked red and move outwards.
- f is a maxscript string value received from enumerating the allFiles collection.
- f must be the argument to Path.GetExtension, a dotnet method, so it is converted to a dotnet string.
- the result of GetExtension (a dotnet string) is converted back to a maxscript string.
- you then construct a System.String using this value, so it requires another conversion.
- Then you call .ToLower on this system.string. The result of this operation is converted back to a maxscript string.
- Then you use this value as an argument to sc.contains, a dotnet method, so it is again converted to a dotnet string.
That’s a total of 5 conversions for each string
You can optimize away some of these conversions by calling your dotnet methods with the asDotNetObject:on parameter, which will prevent the conversion back to a maxscript string.
So, this line will end up as:
for f in allFiles where sc.contains ((sioPath.GetExtension f asDotNetObject:on).toLower asdotnetobject:on) do append files f
Which only has one conversion (from f to dotnet string)
But as we see, pure maxscript is faster in this case.
As expected, a c# assembly is slightly faster:
fn createAssembly =
(
str="using System;
"
str+="using System.IO;
"
str+="using System.Linq;
"
str+="namespace FileTesting
"
str+="{
"
str+="public class DirectorySearcher {
"
str+=" public string[] SearchDirectory(string path, string[] extensions)
"
str+=" {
"
str+=" string[] all = Directory.GetFiles(path, \"*.*\", SearchOption.AllDirectories);
"
str+=" return all.Where(f => extensions.Contains(Path.GetExtension(f).ToLower())).ToArray();
"
str+="}}}
"
local csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
local compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.ReferencedAssemblies.addRange #("System.dll", "System.Core.dll")
compilerParams.GenerateInMemory = on
local compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(str)
global searchAssembly = compilerResults.compiledAssembly
)
fn collectFiles4 folder: extensions: =
(
return (sAssembly.createInstance "FileTesting.DirectorySearcher").SearchDirectory folder extensions
)
lo i not know what to say. Thanks is to enough. I understand what you are using in this assembly (basically 98% from my first code) but I never lear how to write C# code (basic rules). Maybe one day I will do that for sure. Thank you for your time and effort. I really appreciate your work here:applause:
@Mr.lo…it slightly different but tickle me little bit, can I using this kind of way to overrride properties that ussually read only like this kind of example:
aa= dotnetObject "ProfessionalColorTable"
showproperties aa
-- result = .ImageMarginGradientBegin : <System.Drawing.Color>, read-only
can i override , the .image ImageMarginGradientBegin = (dotnetClass “color”).white
in VS its easly done but not in ms…
using System.Drawing;
using System.Windows.Forms;
namespace MyNamespace
{
class toolstriper : ProfessionalColorTable
{
public override Color ButtonSelectedHighlight
{
get { return ButtonSelectedGradientMiddle; }
}
}
}
Sure, just compile it to an assembly on the fly just as with the previous example.