¿Cómo puedo crear un archivo temporal con una extensión específica con .NET?

Necesito generar un archivo temporal único con una extensión .csv.

Lo que hago ahora es

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

Sin embargo, esto no garantiza que mi archivo .csv sea único.

Sé que las posibilidades de que tenga una colisión son muy bajas (especialmente si se tiene en cuenta que no elimino los archivos .tmp), pero este código no me parece bien.

Por supuesto, podría generar manualmente nombres de archivo aleatorios hasta que finalmente encuentre uno único (lo cual no debería ser un problema), pero tengo curiosidad por saber si otros han encontrado una buena manera de lidiar con este problema.

Garantizado para ser (estadísticamente) único:

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

(Para citar el artículo de la wiki sobre la probabilidad de una colisión:

… se estima que el riesgo anual de ser alcanzado por un meteor es una posibilidad en 17 mil millones [19], lo que significa que la probabilidad es de aproximadamente 0.00000000006 (6 × 10-11), lo que equivale a las probabilidades de crear algunas decenas de billones de UUID en un año y teniendo un duplicado. En otras palabras, solo después de generar mil millones de UUID por segundo durante los próximos 100 años, la probabilidad de crear solo un duplicado sería aproximadamente del 50%. La probabilidad de un duplicado sería de aproximadamente el 50% si cada persona en la tierra posee 600 millones de UUID

EDITAR: Por favor, también vea los comentarios de JaredPar.

Prueba esta función …

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

Devolverá una ruta completa con la extensión que elijas.

Tenga en cuenta que no está garantizado que se cree un nombre de archivo único ya que otra persona podría haber creado técnicamente ese archivo. Sin embargo, las posibilidades de que alguien adivine la próxima guía producida por su aplicación y la cree es muy baja. Es bastante seguro asumir que esto será único.

 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); } } } 

Nota: esto funciona como Path.GetTempFileName. Se crea un archivo vacío para reservar el nombre del archivo. Hace 10 bashs, en caso de colisiones generadas por Path.GetRandomFileName ();

También puede usar alternativamente 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"); 

Aquí utilicé una extensión txt pero puede especificar lo que quiera. También configuré la marca de mantener en verdadero para que el archivo temporal se mantenga después de su uso. Lamentablemente, TempFileCollection crea un archivo aleatorio por extensión. Si necesita más archivos temporales, puede crear varias instancias de TempFileCollection.

La documentación de MSDN para GetTempFileName de C ++ analiza su preocupación y la responde:

GetTempFileName no puede garantizar que el nombre del archivo sea único .

Solo se utilizan los 16 bits más bajos del parámetro uUnique. Esto limita GetTempFileName a un máximo de 65.535 nombres de archivo únicos si los parámetros lpPathName y lpPrefixString siguen siendo los mismos.

Debido al algoritmo utilizado para generar nombres de archivos, GetTempFileName puede funcionar mal al crear una gran cantidad de archivos con el mismo prefijo. En tales casos, se recomienda que construya nombres de archivo únicos basados ​​en GUID .

¿Por qué no verificar si el archivo existe?

 string fileName; do { fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv"; } while (System.IO.File.Exists(fileName)); 

Qué tal si:

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

Es altamente improbable que la computadora genere el mismo Guid en el mismo instante. La única debilidad que veo aquí es el impacto en el rendimiento que DateTime.Now.Ticks agregará.

También puedes hacer lo siguiente

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

y esto también funciona como se esperaba

 string filename = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv"); 

En mi opinión, la mayoría de las respuestas propuestas aquí son subóptimas. El que está más cerca es el original propuesto inicialmente por Brann.

Un nombre de archivo temporal debe ser

  • Único
  • Libre de conflictos (no existe ya)
  • Atómico (Creación de nombre y archivo en la misma operación)
  • Difícil de adivinar

Debido a estos requisitos, no es una buena idea progtwigr una bestia por su cuenta. Las personas inteligentes que escriben bibliotecas IO se preocupan por cosas como el locking (si es necesario), etc. Por lo tanto, no veo la necesidad de reescribir System.IO.Path.GetTempFileName ().

Esto, incluso si parece torpe, debería hacer el trabajo:

 //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); 

Esto podría ser útil para ti … Es para crear una temperatura. carpeta y devolverlo como una cadena en VB.NET.

Fácilmente convertible a 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 

Esto parece funcionar bien para mí: comprueba la existencia de archivos y crea el archivo para asegurarse de que sea una ubicación de escritura. Si funciona bien, puede cambiarlo para devolver directamente el FileStream (que normalmente es lo que necesita para un archivo temporal):

 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 

Esto es lo que estoy haciendo:

 string tStamp = String.Format ("{0: yyyyMMdd.HHmmss}", DateTime.Now);
 cadena ProcID = Process.GetCurrentProcess (). Id.ToString ();
 string tmpFolder = System.IO.Path.GetTempPath ();
 string outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";

Esta es una forma simple pero efectiva de generar nombres de archivo incrementales. Verá directamente en la stream (puede señalar fácilmente eso en otro lugar) y buscará archivos con la base YourApplicationName * .txt (de nuevo, puede cambiar eso fácilmente). Comenzará en 0000 para que el primer nombre de archivo sea YourApplicationName0000.txt. si por alguna razón hay nombres de archivos con basura entre (es decir, no números) las partes izquierda y derecha, esos archivos serán ignorados en virtud de la llamada tryparse.

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

Creo que deberías probar esto:

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

Genera un nombre de archivo único y crea un archivo con ese nombre de archivo en una ubicación especificada.