I would like to create a method which takes either a filename as a string
or a FileInfo
and adds an incremented number to the filename if the file exists. But can't quite wrap my head around how to do this in a good way.
For example, if I have this FileInfo
var file = new FileInfo(@"C:\file.ext");
I would like the method to give me a new FileInfo with C:\file 1.ext if C:\file.ext existed, and C:\file 2.ext if C:\file 1.ext existed and so on. Something like this:
public FileInfo MakeUnique(FileInfo fileInfo) { if(fileInfo == null) throw new ArgumentNullException("fileInfo"); if(!fileInfo.Exists) return fileInfo; // Somehow construct new filename from the one we have, test it, // then do it again if necessary. }
public FileInfo MakeUnique(string path) { string dir = Path.GetDirectoryName(path); string fileName = Path.GetFileNameWithoutExtension(path); string fileExt = Path.GetExtension(path); for (int i = 1; ;++i) { if (!File.Exists(path)) return new FileInfo(path); path = Path.Combine(dir, fileName + " " + i + fileExt); } }
Obviously, this is vulnerable to race conditions as noted in other answers.
Lots of good advice here. I ended up using a method written by Marc in an answer to a different question. Reformatted it a tiny bit and added another method to make it a bit easier to use "from the outside". Here is the result:
private static string numberPattern = " ({0})"; public static string NextAvailableFilename(string path) { // Short-cut if already available if (!File.Exists(path)) return path; // If path has extension then insert the number pattern just before the extension and return next filename if (Path.HasExtension(path)) return GetNextFilename(path.Insert(path.LastIndexOf(Path.GetExtension(path)), numberPattern)); // Otherwise just append the pattern to the path and return next filename return GetNextFilename(path + numberPattern); } private static string GetNextFilename(string pattern) { string tmp = string.Format(pattern, 1); if (tmp == pattern) throw new ArgumentException("The pattern must include an index place-holder", "pattern"); if (!File.Exists(tmp)) return tmp; // short-circuit if no matches int min = 1, max = 2; // min is inclusive, max is exclusive/untested while (File.Exists(string.Format(pattern, max))) { min = max; max *= 2; } while (max != min + 1) { int pivot = (max + min) / 2; if (File.Exists(string.Format(pattern, pivot))) min = pivot; else max = pivot; } return string.Format(pattern, max); }
Only partially tested it so far, but will update if I find any bugs with it. (Marcs code works nicely!) If you find any problems with it, please comment or edit or something :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With