Search code examples
c#uniquefilenames

How would you make a unique filename by adding a number?


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.
}

Solution

  • 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 :)