Exclude files from Directory.EnumerateFiles based on multiple criteria - vb.net

Is there a way to exclude files from Directory.EnumerateFiles based on multiple criteria?
The filtering based on extension works fine. But I also want to filter out any filenames which contain any of the exclusion strings. I'm not sure how to do it. I know I could iterate thru the fileArray and check each filename, but that would probably be a performance hit...I'm guessing, right?
This is example code which only uses the first exclusion thumb as a filter. Now I need to use all elements of the exclusion string array. Also, the string used in exclusion is dynamic, it won't necessarily have just the 3 filter criteria. This is just an example.
Dim ext() As String = Split(".jpg;.gif;.bmp;.tif;.png;.tiff;.jpeg", ";")
Dim exclusion() As String = Split("thumb,nothing,empty", ",")
Dim fileArray = From chkFile In Directory.EnumerateFiles(fpath, "*.*", SearchOption.TopDirectoryOnly)
Where ext.Contains(Path.GetExtension(chkFile).ToLower(System.Globalization.CultureInfo.InvariantCulture)) _
And Not Path.GetFileNameWithoutExtension(chkFile).ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains(exclusion(0).Trim.ToLower)

Here's one way that uses FileInfo objects instead of strings. This eliminates all those Path calls, but should be easy to change if desired.
Dim ext() As String = {".jpg", ".gif", ".bmp", ".tif", ".png", ".tiff", ".jpeg"}
Dim exclusion() As String = {"thumb", "nothing", "empty"}
Dim fileArray = From chkFile As FileInfo In New DirectoryInfo(fpath).EnumerateFiles("*.*", SearchOption.TopDirectoryOnly)
Where ext.Contains(chkFile.Extension.ToLower(System.Globalization.CultureInfo.InvariantCulture)) _
And Not exclusion.Any(Function(x) chkFile.Name.ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains(x))
Select chkFile

Related

How to sort list of directories in custom order that follows the calendar month name

I have this list of directories:
JAN_20
FEB_20
MAR_20
.....
DEC_20
I am reading it using the following VB.Net code:
Private Const MY_PATH As String = "\\120.199.10.39\departments\2020\"
Dim Z_directories = Directory.GetDirectories(MY_PATH, "*", SearchOption.AllDirectories).ToList()
For Each dir1 In Z_directories
'do something
Next
The issue is that I would like to "OrderBy" it in a custom way that follows the month order (Jan, Feb, Mar,... etc), not alphabetically or by creation time, etc.
How can I achieve this?
Basically, the thing you need to know here is that you can turn those folder names into Date values like this:
Dim d = Date.ParseExact(s, "MMM_yy", Nothing, DateTimeStyles.None)
That means that you can convert all your folder names to Dates and then sort by those Dates. There are a number of specific ways you can do the actual sort.
Here's an example that uses the array returned by GetDirectories and sorts it in place:
Dim rootFolderPath = "folder path here"
Dim subFolderPaths = Directory.GetDirectories(rootFolderPath)
Array.Sort(subFolderPaths,
Function(sfp1, sfp2)
Dim folderName1 = Path.GetFileName(sfp1)
Dim folderName2 = Path.GetFileName(sfp2)
Dim folderDate1 = Date.ParseExact(folderName1, "MMM_yy", Nothing, DateTimeStyles.None)
Dim folderDate2 = Date.ParseExact(folderName2, "MMM_yy", Nothing, DateTimeStyles.None)
Return folderDate1.CompareTo(folderDate2)
End Function)
Here's a further example that uses a LINQ query to sort before creating a final array:
Dim rootFolderPath = "folder path here"
Dim subFolderPaths = Directory.EnumerateDirectories(rootFolderPath).
OrderBy(Function(sfp) Date.ParseExact(sfp, "MMM_yy", Nothing, DateTimeStyles.None)).
ToArray()
Note that the second example calls EnumerateDirectories rather than GetDirectories, so as to not create two different arrays. Note that you can also call ToList if you prefer a List(Of String) to a String array but I tend to advise using arrays unless you specifically need to add and/or remove items.
I found the solution:
Z_directories .Sort(Function(valueB, valueA) CDate(Right(valueB, 2) & "00-" & Mid(valueB, Len(valueB) - 5, 3) & "-" & "01").CompareTo(CDate(Right(valueA, 2) & "00-" & Mid(valueA, Len(valueA) - 5, 3) & "-" & "01")))

Filter Files and Order By Modify Date VB

I'm filtering files by multiple extensions and i want to order by files date.
I'm trying
GetFiles().OrderByDescending(Function(p) p.LastWriteTime)
but cant order like this.
Dim driver As DriveInfo = DriveInfo.GetDrives().Where(Function(x) x.DriveType = DriveType.Removable).FirstOrDefault
If driver IsNot Nothing AndAlso driver.IsReady Then
UsbRootPath = Path.GetPathRoot(driver.RootDirectory.ToString)
Dim ext = {".png", ".jpg", "*.zip"}
Dim rootFiles() As String = ext.SelectMany(Function(f) Directory.GetFiles(driver.RootDirectory.ToString, f, SearchOption.TopDirectoryOnly)).ToArray()
End If
I'm trying GetFiles().OrderByDescending(Function(p) p.LastWriteTime)
Well, that doesn't work because you use it in a SelectMany, so in kind of a sub-query. You need to apply the ordering last. Also, p is a String, the path, you need File.GetLastWriteTime:
Dim rootFiles() As String = ext.
SelectMany(Function(f) Directory.EnumerateFiles(driver.RootDirectory.ToString, f, SearchOption.TopDirectoryOnly)).
OrderByDescending(Function(f) File.GetLastWriteTime(f)).
ToArray()
I would suggest that you use the DirectoryInfo and FileInfo classes:
Dim fileInfos = extensions.SelectMany(Function(ext) New DirectoryInfo(driver.RootDirectory.FullName).
GetFiles("*" & ext)).
OrderBy(Function(fi) fi.LastWriteTime)
Exactly what you do from there depends on exactly what you want. If you want the full path of each file in an array:
Dim filePaths = fileInfos.Select(Function(fi) fi.FullName).ToArray()

Is there a way to combine these two statements into one?

I was wondering if there is any way in VB.NET where you can use logical operators with 1-dimensional strings.
This is part of my code, and ideally I want to be able to combine the two searches (e.g. file.GetFiles("*.mp4" And "*.wmv")):
For Each f In file.GetFiles("*.mp4")
FileBrowser.Items.Add(f.Name, 5)
i = FileBrowser.FindItemWithText(f.Name).Index
FileBrowser.Items.Item(i).Text = f.Name.Remove(f.Name.Count - f.Extension.Count, f.Extension.Count)
FileBrowser.Items.Item(i).Name = f.FullName
Next
For Each f In file.GetFiles("*.wmv")
FileBrowser.Items.Add(f.Name, 5)
i = FileBrowser.FindItemWithText(f.Name).Index
FileBrowser.Items.Item(i).Text = f.Name.Remove(f.Name.Count - f.Extension.Count, f.Extension.Count)
FileBrowser.Items.Item(i).Name = f.FullName
Next
Can it be done by using a string array or list?
If you put each file extension in an array you can just iterate through it for each extension, and the only thing you'd have to change when adding or deleting extensions is the array itself.
Dim LookForExts() As String = New String() {"*.mp4", "*.wmv", "*.mp3", "*.wav"} 'Add or remove file extensions here.
For Each ext In LookForExts
For Each f In file.GetFiles(ext)
FileBrowser.Items.Add(f.Name, 5)
i = FileBrowser.FindItemWithText(f.Name).Index
FileBrowser.Items.Item(i).Text = f.Name.Remove(f.Name.Count - f.Extension.Count, f.Extension.Count)
FileBrowser.Items.Item(i).Name = f.FullName
Next
Next
For .NET 4.0 and later,
Dim files = Directory.EnumerateFiles("C:\path", "*.*", SearchOption.AllDirectories)
.Where(Function(s) s.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase)
OrElse s.EndsWith(".wmv", StringComparison.OrdinalIgnoreCase))
For earlier versions of .NET,
Dim files = Directory.GetFiles("C:\path", "*.*", SearchOption.AllDirectories)
.Where(Function(s) s.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase)
OrElse s.EndsWith(".wmv", StringComparison.OrdinalIgnoreCase))
Note that 'Directory.EnumerateFiles()' is preferred over 'Directory.GetFiles()' due to performance improvements. Directory.GetFiles() method will work perfectly fine if your directory does not have a huge amount of files within.
You may consume regex in combination with Directory.EnumerateFiles. Try like this:
Regex re = new Regex("\.(mp4|wmv)$");
Dim filteredFiles = Directory.EnumerateFiles(directoryPath, "*.*", SearchOption.AllDirectories).Where(Function(c) re.IsMatch(c))

I need to use Directory.GetFiles but with a few more conditions

Here's an example. Lets say I have two files in a directory:
Filename_v1.pdf
Filename_v1_ABC.pdf
Here's some code:
Dim StorageRoot = "SomePath"
Dim RootName = "Filename"
Dim Suffix = "_v"
Dim matchingFiles as string()
matchingFiles = Directory.GetFiles(StorageRoot, Path.GetFileNameWithoutExtension(file.FileName.ToString) + "*.*")
This returns both files mentioned above, but I need to only match files where the first * in *.* if it is numeric. So I need it to ignore a file if it has anything other than a number after the Suffix.
---- Edit ----
It chewed some of my example. Here's some pseudo code:
matchingFiles = Directory.GetFiles(StorageRoot, Path.GetFileNameWithoutExtension(file.FileName.ToString) + "onlyifitisnumeric.*")
One possibility would be to use Regular expressions in order take only those file whose name is matching the pattern you want to have and filter the result from GetDirectory. The pattern in this case could be:
.*_v[0-9]\.pdf
meaning
.*_v <- all symbols followed by "_v"
[0-9] <- followed by a number
\.pdf <- followed by ".pdf"
The pattern can also use the \d character class that means Matches any decimal digit. and makes the pattern shorter:
.*_v\d\.pdf
And the code can look like this:
dim path = "d:\\temp\\xml\\"
dim files = Directory.GetFiles(path, "*.pdf")
Dim rx = new Regex(".*_v\d\.pdf") ' or Dim rx = new Regex(".*_v[0-9]\.pdf")
for each file in files
if rx.IsMatch(file) then
' do something with the file
Console.WriteLine(file)
end if
next file
If you want your code to be succinct then you could use LINQ to Objects:
dim path = "d:\\temp\\xml\\"
dim files = Directory.GetFiles(path, "*.*")
Dim rx = new Regex(".*\d\.pdf") ' or Dim rx = new Regex(".*[0-9]\.pdf")
dim filteredFiles = files.Where(function(file) rx.IsMatch(file))
Here is a sample:
Imports System.Text.RegularExpressions
Imports System.IO
[...]
Dim rx As New Regex("^filename_v\d$")
Dim f = (From fItem As String In IO.Directory.GetFiles("PathToFiles")
Where rx.IsMatch(IO.Path.GetFileNameWithoutExtension(fItem))
Select fItem).ToArray
' Test Output
MessageBox.Show(String.Join(vbCrLf, f))
Use Regex("^filename_v\d+$") to match numbers of any length.
Here what you can do. First Get files with _v*_*, which possible using regular wildcards. Then, apply additional pattern you need to get the subset of files.
Dim files As IEnumerable(Of String) = _
Directory.GetFiles("your path", "your initial optional wildcard").Where(Function(f) Regex.IsMatch(f, "<your pattern>"))

Adding values to array

I am trying to run an event which will search through the different files in a given directory. The goal is to have it search for all files that begin with 'SP_', which are .sql files containing Stored Procedures. I would then like to add the full text of these Procedures to an array to be used later. This is causing an error when run, which I believe is because 'FullProcedureArray()', the string array I am trying to load does not have defined boundaries. When I declare it as 'FullProcedureArray(7)', or with some other value, it appears to run fine. But I don't want to have to hard-code a boundary for 'FullProcedureArray'; I would rather let it be defined by whatever the number of files in the folder is.
My question: Is there a way to declare 'FullProcedureArray' without having to give it an absolute value? I may just be missing something painfully obvious, but I haven't worked with this type of array much in the past. Thanks in advance for your help.
Dim AppDataLocation As String = "C:\Files\TestFiles\"
Dim ProcedureArray As String()
Dim ProcedureText As String
Dim FullProcedureArray() As String
Dim sourceDirectoryInfo As New System.IO.DirectoryInfo(AppDataLocation)
Dim fileSystemInfo As System.IO.FileSystemInfo
Dim i As Integer = 0
For Each fileSystemInfo In sourceDirectoryInfo.GetFileSystemInfos
If (fileSystemInfo.Name.Contains("SP_")) Then
ProcedureArray = System.IO.File.ReadAllLines(AppDataLocation & fileSystemInfo.Name)
ProcedureText = Join(ProcedureArray, "")
FullProcedureArray.SetValue(ProcedureText, i)
i = (i + 1)
End If
Next
An array by definition has a fixed upper bound. If you don't want a fixed upper bound, don't use an array. Use, for example, a List(Of String) instead:
Dim AppDataLocation As String = "C:\Files\TestFiles\"
Dim ProcedureList As New List(Of String)
Dim sourceDirectoryInfo As New System.IO.DirectoryInfo(AppDataLocation)
For Each fileSystemInfo As System.IO.FileSystemInfo In sourceDirectoryInfo.GetFileSystemInfos
If (fileSystemInfo.Name.Contains("SP_")) Then
Dim ProcedureText As String = _
System.IO.File.ReadAllText(AppDataLocation & fileSystemInfo.Name)
ProcedureList.Add(ProcedureText)
End If
Next
If, for some reason, you still need the result as an array afterwards, simply convert the list to an array:
Dim myArray() As String = ProcedureList.ToArray()
If you don't want to give a size to your array or want to change at runtime, you can use "Redim Preserve"
http://msdn.microsoft.com/en-us/library/w8k3cys2%28v=vs.71%29.aspx