From 1e19e2928ce9b7941a14693676f73529e4ce5f05 Mon Sep 17 00:00:00 2001 From: Jeff Dolan <1824122+iakona@users.noreply.github.com> Date: Mon, 3 Nov 2025 10:11:31 -0600 Subject: [PATCH] Adding a regex based solution for enumerating files looking for multiple extensions at same time (#811) * Adding a regex based solution for enumerating files looking for multiple extensions at same time. This should give better performance than enumerating over the same files multiple times looking for different patterns * Adding a timeout value for regex per security recommendation * Store the regex directly so we don't have to keep constructing it, also adding some logging and handling for exceptions when enumerating files --- Vocaluxe/Base/CSongs.cs | 3 +- VocaluxeLib/CHelper.cs | 71 ++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/Vocaluxe/Base/CSongs.cs b/Vocaluxe/Base/CSongs.cs index 3a442fea..62fe67c3 100644 --- a/Vocaluxe/Base/CSongs.cs +++ b/Vocaluxe/Base/CSongs.cs @@ -348,8 +348,7 @@ namespace Vocaluxe.Base { if (Directory.Exists(path)) { - files = files.Union(CHelper.ListFiles(path, "*.txt", true, true)); - files = files.Union(CHelper.ListFiles(path, "*.txd", true, true)); + files = files.Union(CHelper.ListTextFiles(path, true, true)); } } } diff --git a/VocaluxeLib/CHelper.cs b/VocaluxeLib/CHelper.cs index 35a6ae5c..6164b85c 100644 --- a/VocaluxeLib/CHelper.cs +++ b/VocaluxeLib/CHelper.cs @@ -21,27 +21,16 @@ using System.Drawing; using System.Globalization; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using VocaluxeLib.Log; namespace VocaluxeLib { public static class CHelper { - private static readonly List _SoundFileTypes = new List - { - "*.mp3", - "*.wma", - "*.ogg", - "*.wav" - }; - - private static readonly List _ImageFileTypes = new List - { - "*.jpg", - "*.jpeg", - "*.png", - "*.gif" - }; + private static readonly Regex _TextFileTypesRegex = new Regex("\\.txt|\\.txd", RegexOptions.IgnoreCase); + private static readonly Regex _SoundFileTypesRegex = new Regex("\\.mp3|\\.wma|\\.ogg|\\.wav", RegexOptions.IgnoreCase); + private static readonly Regex _ImageFileTypesRegex = new Regex("\\.jpg|\\.jpeg|\\.png|\\.gif", RegexOptions.IgnoreCase); public static int CombinationCount(int n, int k) { @@ -186,22 +175,58 @@ namespace VocaluxeLib files = Directory.EnumerateFiles(dir.FullName, searchPattern, recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); files = files.Select(fullpath ? (Func)Path.GetFullPath : Path.GetFileName); } - catch (Exception) {} + catch (Exception e) + { + CLog.Error(e, "Error listing files for path " + path + " with search pattern " + searchPattern); + return Enumerable.Empty(); + } return files; } /// - /// Returns a list with all files in the given path that matches at least one of the given patterns + /// Returns a list with all files in the given path that match a given extension pattern /// - /// Path to search for - /// List of patterns to match + /// Path to search + /// File extension regex to match (e.g. "\\.jpg|\\.png") /// Search directories recursively /// False for just file names, True for full path /// List of file names - public static IEnumerable ListFiles(string path, IEnumerable searchPatterns, bool recursive = false, bool fullpath = false) + public static IEnumerable ListFilesForExtPattern(string path, Regex extPattern, bool recursive = false, bool fullpath = false) { - return searchPatterns.SelectMany(pattern => ListFiles(path, pattern, recursive, fullpath)); + IEnumerable files = Enumerable.Empty(); + var dir = new DirectoryInfo(path); + if (!dir.Exists) + return files; + + if (recursive && !fullpath) + throw new NotSupportedException("recursive file listing with relative path names is not supported"); + + try + { + files = Directory.EnumerateFiles(path, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) + .Where(file => extPattern.IsMatch(Path.GetExtension(file))); + files = files.Select(fullpath ? (Func)Path.GetFullPath : Path.GetFileName); + } + catch (Exception e) + { + CLog.Error(e, "Error listing files for path " + path + " with regex " + extPattern.ToString()); + return Enumerable.Empty(); + } + + return files; + } + + /// + /// Returns a list with all text files in the given path + /// + /// Path to search for + /// Search directories recursively + /// False for just file names, True for full path + /// List of text file names + public static IEnumerable ListTextFiles(string path, bool recursive = false, bool fullpath = false) + { + return ListFilesForExtPattern(path, _TextFileTypesRegex, recursive, fullpath); } /// @@ -213,7 +238,7 @@ namespace VocaluxeLib /// List of image file names public static IEnumerable ListImageFiles(string path, bool recursive = false, bool fullpath = false) { - return ListFiles(path, _ImageFileTypes, recursive, fullpath); + return ListFilesForExtPattern(path, _ImageFileTypesRegex, recursive, fullpath); } /// @@ -225,7 +250,7 @@ namespace VocaluxeLib /// List of image file names public static IEnumerable ListSoundFiles(string path, bool recursive = false, bool fullpath = false) { - return ListFiles(path, _SoundFileTypes, recursive, fullpath); + return ListFilesForExtPattern(path, _SoundFileTypesRegex, recursive, fullpath); } public static bool TryParse(string value, out T result, bool ignoreCase = false)