using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; using System.Text.RegularExpressions; using Spire.Xls; namespace bdf { class CXLFile { public static List S_ServiceOrders = new List { }; public static void S_getXLS(string task, string dir) { //string folderPath = dir; string filePrefix1 = "ROGERS_"; string filePrefix2 = task + "_SO"; var excelFiles = getLatestVersionFiles(dir, filePrefix1, filePrefix2); if ( (excelFiles.Length == 0) || (excelFiles == null) ) { Console.WriteLine("No Excel files found with prefix: " + filePrefix1 + "*" + filePrefix2); //throw new System.ArgumentNullException("filePaths"); return; } foreach (string path in excelFiles) { if (string.IsNullOrWhiteSpace(path)) continue; if (!System.IO.File.Exists(path)) throw new System.IO.FileNotFoundException( "Excel file not found.", path); Logger.Log(1, "Scanning: {0}", Path.GetFileName(path)); // Create a new workbook and load the file Spire.Xls.Workbook wb = new Spire.Xls.Workbook(); //straight up load from file //wb.LoadFromFile(path); // FreeSpire autodetects .xls vs .xlsx //more network savvy way that handles sharepoint's idiosyncrasies FileStream stream = null; // OneDrive sync engine frequently locks files briefly: const int maxRetries = 12; const int retryDelayMs = 150; for (int i = 0; i < maxRetries; i++) { try { stream = new FileStream( path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete ); break; // opened successfully } catch (IOException) { if (i == maxRetries - 1) throw; System.Threading.Thread.Sleep(retryDelayMs); } } wb.LoadFromStream(stream); Logger.Log(5, " -loaded SO={0}", wb.Worksheets[1][2, 1].Value.ToString() ); S_ServiceOrders.Add(wb); } Logger.Log("All SO Excel files processed successfully."); return; // S_ServiceOrders; } // This routine is wholly dependent on the filename structure being ROGERS*LDF*_SO*VER*number // in future should just open each workbook and read exact version from the first worksheet. public static string[] getLatestVersionFiles(string folder, string prefixA, string prefixB) { var files = Directory.GetFiles(folder); // Build wildcard pattern: // file must contain prefixA ... anything ... prefixB Func matchesPrefix = name => name.IndexOf(prefixA, StringComparison.OrdinalIgnoreCase) >= 0 && name.IndexOf(prefixB, StringComparison.OrdinalIgnoreCase) > name.IndexOf(prefixA, StringComparison.OrdinalIgnoreCase); var filtered = files .Where(f => matchesPrefix(Path.GetFileName(f))) .ToList(); // Serial = 6 digits // After "VER" allow ANY non-digit characters, then the version number var regex = new Regex( @"(?\d{6}).*?VER\D*(?\d+)", RegexOptions.IgnoreCase ); Dictionary best = new Dictionary(); foreach (var file in files) //todo should this be filtered instead? { string name = Path.GetFileName(file); var match = regex.Match(name); if (!match.Success) { Logger.Log(1, " Did not find SO and Version for file {0}", name); continue; } string serial = match.Groups["serial"].Value; if (!int.TryParse(match.Groups["ver"].Value, out int ver)) { Logger.Log(1, " Did not extract Version for file {0}", name); continue; } // Keep highest version per serial if (!best.ContainsKey(serial)) // || ver > best[serial].version) { best[serial] = (ver, file); Logger.Log(5, " Adding: {0} ver {1}", serial, ver); //todo open up file and verify so version from internals? } else if (ver > best[serial].version) { Logger.Log(2, " Replacing: {0} ver {1} with ver {2}", serial, best[serial].version, ver); best[serial] = (ver, file); } else { Logger.Log(4, " Ignoring: {0} v.{1}", serial, ver); } } return best.Values.Select(v => v.file).ToArray(); } } }