4

Is there a built in .NET function to get a unique filename if a filename already exists? So if I try and save MyDoc.doc and it already exists, the file will save with name MyDoc(1).doc, the same way a browser download works for example.

If not, what is the most efficient way to achieve this result?

I am using the File.Move function at the moment btw.

fearofawhackplanet
  • 52,166
  • 53
  • 160
  • 253
  • 3
    System.IO.Path.GetTempFileName() will work, but if you want it human readable that might not be your best option. – Kendrick Jun 22 '10 at 12:29
  • @Kendrick: It looks like the OP may be using this for the initial write, but then wants to rename it in a human-readable way. – Steven Sudit Jun 22 '10 at 15:42

4 Answers4

3

EDIT:

Here's another solution I came up with after Steven Sudit's comment:

static void Main(string[] args)
{
    CopyFile(new FileInfo(@"D:\table.txt"), new FileInfo(@"D:\blah.txt"));
}

private static void CopyFile(FileInfo source, FileInfo destination)
{
    int attempt = 0;

    FileInfo originalDestination = destination;

    while (destination.Exists || !TryCopyTo(source, destination))
    {
        attempt++;
        destination = new FileInfo(originalDestination.FullName.Remove(
            originalDestination.FullName.Length - originalDestination.Extension.Length)
            + " (" + attempt + ")" + originalDestination.Extension);
    }
}

private static bool TryCopyTo(FileInfo source, FileInfo destination)
{
    try
    {
        source.CopyTo(destination.FullName);
        return true;
    }
    catch
    {
        return false;
    }
}
Codesleuth
  • 10,321
  • 8
  • 51
  • 71
  • This would work, but I'm not entirely happy with it. One undesirable characteristic is that it's going to throw as many exceptions as there are conflicts. I'd rather do File.Exists until I find an opening, then use File.Move. If the move fails, I'd increment my filename and retry. This handles the race condition as an exceptional case, rather than the typical one. – Steven Sudit Jun 22 '10 at 13:26
  • @Steven Sudit true that could also work, but `File.Move` will also throw exceptions (the same ones actually). Maybe it would be worth writing a version of this that does what you say. – Codesleuth Jun 22 '10 at 14:06
  • 1
    @Steven Sudit: changed the answer - you were completely right about the previous one. The new one looks to me like it makes more sense. – Codesleuth Jun 22 '10 at 14:24
  • I like your changes. I would still suggest a few things, if you don't mind: 1) I get the impression from the OP that the file has already been written to a temp name and just needs to be renamed to its final one. If so, then you would use Move instead of Copy. 2) File offers higher performance than FileInfo, although perhaps at a small cost to convenience. 3) From my own recent experience, I've found that regexp with matching groups, like Andrey suggested, are more flexible. You can start with a name like "file (3).txt" and still work. – Steven Sudit Jun 22 '10 at 14:59
2

check the name against Regex *.\(\d+\), if it doesn't match, add (1), if it matches increment the number in brackets.

Andrey
  • 59,039
  • 12
  • 119
  • 163
  • 1
    You could probably leverage the regexp so that the digits would form a match group, making it easier to isolate and replace them. – Steven Sudit Jun 22 '10 at 12:29
  • 1
    @Steven Sudit yes, yes. this was something like proof of concept. – Andrey Jun 22 '10 at 12:33
  • 2
    Doing anything by checking if it exists before creating it causes potential race conditions. The file should be locked if it was successfully found not to exist, as in my answer. – Codesleuth Jun 22 '10 at 12:39
  • And it's fine for that. I recently wrote some code that checked whether a directory name matched "New folder" in the general case, and the regexp for that was surprisingly complex. What you're suggesting involves rebuilding, not just detecting, so it's even more so. Having said that, I think this is the very best solution in terms of flexibility. – Steven Sudit Jun 22 '10 at 15:01
1

I don't know, but it's not hard to build one yourself:

if filename does not exists then 
    save file as filename
else
n = 1
while filename(n) exists: 
    n += 1
save file as filename(n)
Lie Ryan
  • 62,238
  • 13
  • 100
  • 144
1

As the other answers shows, there are multiple ways of doing this, but one thing to be aware of is if other processes than your can create files you have to be careful, since if you check that a filename is available, by the time you save your new file, some other process might already have saved a file using that name and you'll overwrite that file.

Hans Olsson
  • 54,199
  • 15
  • 94
  • 116
  • This is a race condition and if your software has security implication then consider malicious attempts to trigger exceptions. – softveda Jun 22 '10 at 12:43
  • I added an answer that solves this, but I clarify how it can be improved (although I say if I created it, it's mine ;] ) – Codesleuth Jun 22 '10 at 12:45