328

I need to generate a unique temporary file with a .csv extension.

What I do right now is

string filepath = System.IO.Path.GetTempFileName().Replace(".tmp", ".csv");

However, this doesn't guarantee that my .csv file will be unique.

I know the chances I ever got a collision are very low (especially if you consider that I don't delete the .tmp files), but this code doesn't looks good to me.

Of course I could manually generate random file names until I eventually find a unique one (which shouldn't be a problem), but I'm curious to know if others have found a nice way to deal with this problem.

sergiol
  • 4,122
  • 4
  • 47
  • 81
Brann
  • 31,689
  • 32
  • 113
  • 162
  • 5
    some caveats about GetTempFileName The GetTempFileName method will raise an IOException if it is used to create more than 65535 files without deleting previous temporary files. The GetTempFileName method will raise an IOException if no unique temporary file name is available. To resolve this error, delete all unneeded temporary files. – ChatGPT Nov 09 '12 at 07:28
  • 2
    Temporary files are mainly uses for a specific set of conditions. If the file extension is important, I wonder if maybe using the GetTempFileName isn't the write solution. I know it's been a long time, but if you told us more about the context and need for these files, we might be able to suggest a better approach altogether. more here: http://support.microsoft.com/kb/92635?wa=wsignin1.0 – ChatGPT Nov 09 '12 at 07:33
  • 1
    Keep in mind `GetTempFileName()` *creates* a new file each time you call it. -- If you immediately change the string to something else, you just created a new zero byte file in your temp directory (and as others have noted, this will eventually cause it to fail when you hit 65535 files in there...) -- To avoid this, make sure to delete any files that you create in that folder (including the ones returned by `GetTempFileName()`, ideally in a finally block). – BrainSlugs83 Oct 23 '18 at 23:53

18 Answers18

422

Guaranteed to be (statistically) unique:

string fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv"; 

(To quote from the wiki article on the probabilty of a collision:

...one's annual risk of being hit by a meteorite is estimated to be one chance in 17 billion [19], that means the probability is about 0.00000000006 (6 × 10−11), equivalent to the odds of creating a few tens of trillions of UUIDs in a year and having one duplicate. In other words, only after generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%. The probability of one duplicate would be about 50% if every person on earth owns 600 million UUIDs

EDIT: Please also see JaredPar's comments.

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
  • 34
    But not guaranteed to be in a writable location – JaredPar Feb 24 '09 at 12:35
  • 5
    System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv" will work. @Mitch : could you please update your answer before I mark it as accepted? thanks. – Brann Feb 24 '09 at 12:38
  • 35
    And they are **not** guaranteed to be unique at all, just statistically unlikely. – paxdiablo Feb 24 '09 at 12:39
  • 48
    @Pax: you have more chnace of winning the lottery 1000 times in a row than generating two idebtical guids. That's unique enough I guess... – Mitch Wheat Feb 24 '09 at 12:41
  • 5
    No, Mitch, it's either guaranteed or not. In this case, it's not. The probability of me winning the lottery 1000 times in a row (or a million) is not zero. – paxdiablo Feb 24 '09 at 12:44
  • 7
    @Pax: let me know when you generate 2 in a row: I think the estimate was age of the universe... – Mitch Wheat Feb 24 '09 at 12:49
  • 3
    As you wish, Mitch. I can't in all honesty vote up an incorrect answer, especially when it's so easy to fix. So I'll move on. – paxdiablo Feb 24 '09 at 12:51
  • 12
    @Mitch the reason it's not unique is because it's *possible* for me to simply create a file with the same name in the same path. GUIDs while guaranteed to be unique are also predictable which means given enough information I could guess the next set of guids generated by your box – JaredPar Feb 24 '09 at 12:57
  • 4
    @Mitch (cont) as such I could create a file with the same name before you return the GUID. When dealing with the filesystem, very little if anything, is guaranteed. I consider this to be a corner case though because you really need a mean spirited user to *beat* your app. I did not -1 you – JaredPar Feb 24 '09 at 12:58
  • 1
    ...and of course it depends on the system implmentation of the GUID generator and presumably the pseudo-random generator. I believe some systems use network packet traffic to help randomise... – Mitch Wheat Feb 24 '09 at 13:02
  • 3
    @Mitch see (http://en.wikipedia.org/wiki/GUID) it discusses the predictability (possible to predict next 250,000 guids). The probability of collision is not the issue here, it's the potential for prediction of the next GUIDs – JaredPar Feb 24 '09 at 13:03
  • @JaredPar: that's an excellent point and one I'd overlooked. Thanks. – Mitch Wheat Feb 24 '09 at 13:24
  • 1
    Thought I'd swing back for a quick look. Downvote removed, @Mitch, and upvote added. – paxdiablo Feb 24 '09 at 22:33
  • 2
    Better option is to do it in a loop, so that if file exists then generate a new one. – Migol Feb 15 '12 at 14:50
  • 6
    You should use `Path.Combine(String, String)` for this. `Path.GetTempPath()` is not guaranteed to return a trailing path separator as it is based on environment variables. – Brett Ryan Oct 23 '12 at 10:32
  • 3
    @BrettRyan: That is incorrect. According to the docs, it checks environment vars in this order TMP, TEMP, USERPROFILE, the windows directory. My environment var TMP (and TEMP) do not have trailing slashes, but a call to System.IO.Path.GetTempPath() appends a trailing slash. – Mitch Wheat Oct 23 '12 at 11:45
  • 273
    good lord people, try harder to keep your head out of the clouds. The approach is: generate a random file name, then create it if it doesn't exist. So just help him code that nicely. All this talk about pseudo-random generators and universally unique numbers is totally unnecessary. – ChatGPT Nov 09 '12 at 07:14
  • 1
    String.Format is the way to go: string.Format("{0}.csv", Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); – Doguhan Uluca Mar 11 '13 at 15:24
  • @Doguhan Uluca : for 2 arguments, it doesn't matter in terms of speed. Also, I wanted it to be short and clear. I would use Path.Combine() in production code. – Mitch Wheat Mar 12 '13 at 00:45
  • 7
    Needs more `Path.Combine()` – BlueRaja - Danny Pflughoeft Nov 15 '13 at 19:57
  • 9
    Path.Combine is better, i'd probably even use changeExtension Path.ChangeExtension(Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString()), "csv"); – Angus Connell Mar 05 '14 at 09:40
  • 3
    anyone questioning the 'uniqueness' of properly-generated GUID's should read up on FIT (failures in time) analysis. You will quickly start thinking of all the other things that will** go wrong first. Say, unrecoverable memory corruption, even in your ECC server.... – some bits flipped Jan 30 '17 at 01:30
  • You should be using `Path.Combine` and not simply concatenating the strings. – BrainSlugs83 Oct 23 '18 at 23:55
  • @Brainslugs83: I think if you benchmark, you'll find that for a small number of strings, the difference is immaterial. I suggest you do some research before downvoting. Thanks. – Mitch Wheat Oct 24 '18 at 01:34
  • 3
    "So you're telling me there's a chance...?" – Jordan Ryder Oct 28 '19 at 16:42
  • @Jordon: a chance of a collision? Yes, but you have more chance of winning the lottery several times in a row... – Mitch Wheat Oct 29 '19 at 09:22
  • 2
    Alternatively you could code like this: `string fileName = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv");` – Beauty Nov 26 '20 at 14:03
67

Try this function ...

public static string GetTempFilePathWithExtension(string extension) {
  var path = Path.GetTempPath();
  var fileName = Path.ChangeExtension(Guid.NewGuid().ToString(), extension);
  return Path.Combine(path, fileName);
}

It will return a full path with the extension of your choice.

Note, it's not guaranteed to produce a unique file name since someone else could have technically already created that file. However the chances of someone guessing the next guid produced by your app and creating it is very very low. It's pretty safe to assume this will be unique.

Davide Pizzolato
  • 679
  • 8
  • 25
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
58
public static string GetTempFileName(string extension)
{
  int attempt = 0;
  while (true)
  {
    string fileName = Path.GetRandomFileName();
    fileName = Path.ChangeExtension(fileName, extension);
    fileName = Path.Combine(Path.GetTempPath(), fileName);

    try
    {
      using (new FileStream(fileName, FileMode.CreateNew)) { }
      return fileName;
    }
    catch (IOException ex)
    {
      if (++attempt == 10)
        throw new IOException("No unique temporary file name is available.", ex);
    }
  }
}

Note: this works like Path.GetTempFileName. An empty file is created to reserve the file name. It makes 10 attempts, in case of collisions generated by Path.GetRandomFileName();

Maxence
  • 12,868
  • 5
  • 57
  • 69
  • Keep in mind that [`Path.GetRandomFileName()`](https://msdn.microsoft.com/en-us/library/system.io.path.gettempfilename(v=vs.110).aspx) actually creates a zero-byte file on disk and returns the full path of that file. You are not using this file, only its name to change the extension. So, if you don't make sure that you are deleting these temporary files, after 65535 calls to this function it will begin to fail. – Vladimir Baranov Aug 10 '17 at 03:07
  • 12
    You have mixed `GetTempFileName()` and `GetRandomFileName()`. `GetTempFileName()` create a zero-byte file like my method, but `GetRandomFileName()` does not create a file. From the docs : > Unlike GetTempFileName, GetRandomFileName does not create a file. Your link points to the wrong page. – Maxence Aug 11 '17 at 08:58
19

You can also alternatively use System.CodeDom.Compiler.TempFileCollection.

string tempDirectory = @"c:\\temp";
TempFileCollection coll = new TempFileCollection(tempDirectory, true);
string filename = coll.AddExtension("txt", true);
File.WriteAllText(Path.Combine(tempDirectory,filename),"Hello World");

Here I used a txt extension but you can specify whatever you want. I also set the keep flag to true so that the temp file is kept around after use. Unfortunately, TempFileCollection creates one random file per extension. If you need more temp files, you can create multiple instances of TempFileCollection.

Mehmet Aras
  • 5,284
  • 1
  • 25
  • 32
11

The MSDN documentation for C++'s GetTempFileName discusses your concern and answers it:

GetTempFileName is not able to guarantee that the file name is unique.

Only the lower 16 bits of the uUnique parameter are used. This limits GetTempFileName to a maximum of 65,535 unique file names if the lpPathName and lpPrefixString parameters remain the same.

Due to the algorithm used to generate file names, GetTempFileName can perform poorly when creating a large number of files with the same prefix. In such cases, it is recommended that you construct unique file names based on GUIDs.

jnm2
  • 7,960
  • 5
  • 61
  • 99
Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
  • 2
    The GetTempFileName method will raise an IOException if it is used to create more than 65535 files without deleting previous temporary files. The GetTempFileName method will raise an IOException if no unique temporary file name is available. To resolve this error, delete all unneeded temporary files. – ChatGPT Nov 09 '12 at 07:39
  • That is an incomplete quote. The relevant quote: "*If uUnique is not zero*, you must create the file yourself. Only a file name is created, because GetTempFileName is not able to guarantee that the file name is unique." If you call it the way everyone is discussing here, uUnique will be zero. – jnm2 Dec 05 '14 at 14:24
10

Why not checking if the file exists?

string fileName;
do
{
    fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
} while (System.IO.File.Exists(fileName));
Tute
  • 6,943
  • 12
  • 51
  • 61
  • 12
    File.Exists tells you information about the past and is hence not reliable. In between the return of File.Exist and your code executing it's possible for the file to be created. – JaredPar Feb 24 '09 at 13:42
  • 4
    ... then you're safe with your own program maybe but not with other process writing a file on that exact same destination... – Koen Jul 29 '11 at 10:17
  • @JaredPar and what is propability of this happening? – Migol Feb 16 '12 at 11:48
  • 2
    @Migol It's very low, and by definition an exceptional condition. Hmmm, exactly what exceptions were designed to be used for. – Cody Gray - on strike Aug 20 '12 at 11:44
  • 3
    @CodyGray chance for guid clash is 1/2^128. Chance that it would happen 2 times is 1/2^256 etc. Dont bother! – Migol Aug 23 '12 at 08:03
7

You can also do the following

string filepath = Path.ChangeExtension(Path.GetTempFileName(), ".csv");

and this also works as expected

string filepath = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv");
sergiol
  • 4,122
  • 4
  • 47
  • 81
Michael Prewecki
  • 2,064
  • 4
  • 16
  • 28
  • 2
    this will fail if there is a file temp.csv and you create temp.tmp and then change the extension to csv – David May 26 '09 at 13:46
  • No it won't...GetTempFileName() creates a unique filename...upto some limit of 32K at which point you need to delete some files but I think my solution is correct. It's wrong if I were to pass a file path into ChangeExtension that isn't guaranteed to be unique, but that's not what my solution does. – Michael Prewecki May 27 '09 at 04:23
  • 11
    GetTempFileName guarantees that the path it returns will be unique. Not that the path it returns + ".csv" will be unique. Changing the extension in this way could fail as David said. – Marcus Griep Jul 14 '09 at 02:10
  • 5
    GetTempFileName creates a file, so your first example is a resource leak. – Gary McGill Sep 24 '12 at 14:05
  • The first solution should have a warning, as it may eventually cause IOException "The file exists" when there are too many files in the %TEMP% directory.. The problem is that GetTempFileName() creates a physical empty file and returns its name. ChangeExtension() changes the string name, but not the name of the physical file, so the orginal .tmp file gets forgotten / leaks.. – evgenybf Jun 27 '20 at 10:28
6

How about:

Path.Combine(Path.GetTempPath(), DateTime.Now.Ticks.ToString() + "_" + Guid.NewGuid().ToString() + ".csv")

It is highly improbable that the computer will generate the same Guid at the same instant of time. The only weakness i see here is the performance impact DateTime.Now.Ticks will add.

I.R.Baboon
  • 61
  • 1
  • 1
3

In my opinion, most answers proposed here as sub-optimal. The one coming closest is the original one proposed initially by Brann.

A Temp Filename must be

  • Unique
  • Conflict-free (not already exist)
  • Atomic (Creation of Name & File in the same operation)
  • Hard to guess

Because of these requirements, it is not a godd idea to program such a beast on your own. Smart People writing IO Libraries worry about things like locking (if needed) etc. Therefore, I see no need to rewrite System.IO.Path.GetTempFileName().

This, even if it looks clumsy, should do the job:

//Note that this already *creates* the file
string filename1 = System.IO.Path.GetTempFileName()
// Rename and move
filename = filename.Replace(".tmp", ".csv");
File.Move(filename1 , filename);
  • 3
    But that's not conflict free, the 'csv' could already exist and be overwritten. – xvan Jul 11 '16 at 20:29
  • 3
    `File.Move` raises `IOException` if the destination file already exists. https://msdn.microsoft.com/en-us/library/system.io.file.move(v=vs.110).aspx – joshuanapoli Mar 09 '17 at 17:33
3

I mixed @Maxence and @Mitch Wheat answers keeping in mind I want the semantic of GetTempFileName method (the fileName is the name of a new file created) adding the extension preferred.

string GetNewTempFile(string extension)
{
    if (!extension.StartWith(".")) extension="." + extension;
    string fileName;
    bool bCollisions = false;
    do {
        fileName = Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString() + extension);
        try
        {
            using (new FileStream(fileName, FileMode.CreateNew)) { }
            bCollisions = false;
        }
        catch (IOException)
        {
            bCollisions = true;
        }
    }
    while (bCollisions);
    return fileName;
}
Fil
  • 1,032
  • 13
  • 29
2

This could be handy for you... It's to create a temp. folder and return it as a string in VB.NET.

Easily convertible to C#:

Public Function GetTempDirectory() As String
    Dim mpath As String
    Do
        mpath = System.IO.Path.Combine(System.IO.Path.GetTempPath, System.IO.Path.GetRandomFileName)
    Loop While System.IO.Directory.Exists(mpath) Or System.IO.File.Exists(mpath)
    System.IO.Directory.CreateDirectory(mpath)
    Return mpath
End Function
Simon
  • 183
  • 2
  • 7
1

Based on answers I found from the internet, I come to my code as following:

public static string GetTemporaryFileName()
{       
    string tempFilePath = Path.Combine(Path.GetTempPath(), "SnapshotTemp");
    Directory.Delete(tempFilePath, true);
    Directory.CreateDirectory(tempFilePath);
    return Path.Combine(tempFilePath, DateTime.Now.ToString("MMddHHmm") + "-" + Guid.NewGuid().ToString() + ".png");
}

And as C# Cookbook by Jay Hilyard, Stephen Teilhet pointed in Using a Temporary File in Your Application:

  • you should use a temporary file whenever you need to store information temporarily for later retrieval.

  • The one thing you must remember is to delete this temporary file before the application that created it is terminated.

  • If it is not deleted, it will remain in the user’s temporary directory until the user manually deletes it.

roschach
  • 8,390
  • 14
  • 74
  • 124
Speedoops
  • 126
  • 2
1

This seems to work fine for me: it checks for file existance and creates the file to be sure it's a writable location. Should work fine, you can change it to return directly the FileStream (which is normally what you need for a temp file):

private string GetTempFile(string fileExtension)
{
  string temp = System.IO.Path.GetTempPath();
  string res = string.Empty;
  while (true) {
    res = string.Format("{0}.{1}", Guid.NewGuid().ToString(), fileExtension);
    res = System.IO.Path.Combine(temp, res);
    if (!System.IO.File.Exists(res)) {
      try {
        System.IO.FileStream s = System.IO.File.Create(res);
        s.Close();
        break;
      }
      catch (Exception) {

      }
    }
  }
  return res;
} // GetTempFile
Paolo Iommarini
  • 219
  • 2
  • 9
0

This is a simple but effective way to generate incremental filenames. It will look in the current directly (you can easily point that somewhere else) and search for files with the base YourApplicationName*.txt (again you can easily change that). It will start at 0000 so that the first file name will be YourApplicationName0000.txt. if for some reason there are file names with junk between (meaning not numbers) the left and right parts, those files will be ignored by virtue of the tryparse call.

    public static string CreateNewOutPutFile()
    {
        const string RemoveLeft = "YourApplicationName";
        const string RemoveRight = ".txt";
        const string searchString = RemoveLeft + "*" + RemoveRight;
        const string numberSpecifier = "0000";

        int maxTempNdx = -1;

        string fileName;
        string [] Files = Directory.GetFiles(Directory.GetCurrentDirectory(), searchString);
        foreach( string file in Files)
        {
            fileName = Path.GetFileName(file);
            string stripped = fileName.Remove(fileName.Length - RemoveRight.Length, RemoveRight.Length).Remove(0, RemoveLeft.Length);
            if( int.TryParse(stripped,out int current) )
            {
                if (current > maxTempNdx)
                    maxTempNdx = current;
            }
        }
        maxTempNdx++;
        fileName = RemoveLeft + maxTempNdx.ToString(numberSpecifier) + RemoveRight;
        File.CreateText(fileName); // optional
        return fileName;
    }
Display name
  • 1,228
  • 1
  • 18
  • 29
0

Easy Function in C#:

public static string GetTempFileName(string extension = "csv")
{
    return Path.ChangeExtension(Path.GetTempFileName(), extension);
}
Ranch Camal
  • 501
  • 1
  • 4
  • 12
  • GetTempFileName() creates a temporary file in the temp folder. As a result you will have two temporary files created, one of which will leak. – evgenybf Jun 19 '20 at 11:01
  • @evgenybf , We are not creating two files. We are generating a file then changing the extension. – Ranch Camal Jun 25 '20 at 21:48
  • 2
    you are wrong. Check out the documentation: https://learn.microsoft.com/en-us/dotnet/api/system.io.path.gettempfilename?view=netcore-3.1 GetTempFileName() creates a file and returns its name. Then, you take its name and change its extension. But it only affects the string name, not the name of the physical file on the disc. Then you return the modified name, leaving temporary file on the disc forgotten. – evgenybf Jun 27 '20 at 10:10
  • 1
    Call the method several times and look into your %TEMP% directory. There will be many empty .tmp files there. After a while, when there are too many tmp files (>~60k), GetTempFileName() will start throwing IOException "The file exists". – evgenybf Jun 27 '20 at 10:19
0

This is what I am doing:

string tStamp = String.Format("{0:yyyyMMdd.HHmmss}", DateTime.Now);
string ProcID = Process.GetCurrentProcess().Id.ToString();
string tmpFolder = System.IO.Path.GetTempPath();
string outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";
Michael Fitzpatrick
  • 682
  • 3
  • 8
  • 18
  • Good: includes process identifier Bad: does not include thread identifier (though you could run this inside a lock) Bad: timestamp is only 1 second resolution. In many, many applications, it’s common to produce many files per second. – andrewf Oct 29 '14 at 16:05
0

In this what we can do we can first find the extension of file which is coming from file and after finding its extension.Then we can create the temprary name of file and after that we can change extension by the previous one it will works.

var name = Path.GetTempFileName();
var changename = Path.GetFileName(name);
var fileName = Path.ChangeExtension(changename, fileExtension);
Esset
  • 916
  • 2
  • 15
  • 17
-2

I think you should try this:

string path = Path.GetRandomFileName();
path = Path.Combine(@"c:\temp", path);
path = Path.ChangeExtension(path, ".tmp");
File.Create(path);

It generates a unique filename and creates a file with that file name at a specified location.

McDowell
  • 107,573
  • 31
  • 204
  • 267
Smita
  • 21
  • 1
  • 3
    This solution has so many problems with it. you can't combine C:\temp with an absolute path, c:\temp may not be writeable, and it doesn't guarantee that the .tmp version of the file is unique. – Mark Lakata Nov 16 '12 at 22:36