Come posso creare un file temporaneo con un'estensione specifica con .NET?


277

Devo generare un file temporaneo univoco con estensione .csv.

Quello che faccio adesso è

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

Tuttavia, ciò non garantisce che il mio file .csv sarà univoco.

So che le probabilità che io abbia mai avuto una collisione sono molto basse (specialmente se si considera che non elimino i file .tmp), ma questo codice non mi sembra buono.

Ovviamente potrei generare manualmente nomi di file casuali fino a quando non ne trovo uno unico (che non dovrebbe essere un problema), ma sono curioso di sapere se altri hanno trovato un modo carino per affrontare questo problema.


4
alcune avvertenze su GetTempFileName Il metodo GetTempFileName genererà una IOException se viene utilizzato per creare più di 65535 file senza eliminare i file temporanei precedenti. Il metodo GetTempFileName genererà una IOException se non è disponibile un nome file temporaneo univoco. Per risolvere questo errore, eliminare tutti i file temporanei non necessari.
Max Hodges,

2
I file temporanei sono principalmente utilizzati per un insieme specifico di condizioni. Se l'estensione del file è importante, mi chiedo se forse usare GetTempFileName non sia la soluzione di scrittura. So che è passato molto tempo, ma se ci dicessi di più sul contesto e sulla necessità di questi file, potremmo essere in grado di suggerire un approccio migliore del tutto. altro qui: support.microsoft.com/kb/92635?wa=wsignin1.0
Max Hodges

1
Tieni presente che GetTempFileName() crea un nuovo file ogni volta che lo chiami. - Se cambi immediatamente la stringa in qualcos'altro, hai appena creato un nuovo file a zero byte nella tua directory temporanea (e come altri hanno notato, questo alla fine causerà il fallimento quando colpisci 65535 file lì dentro ...) - - Per evitare ciò, assicurati di eliminare tutti i file che crei in quella cartella (inclusi quelli restituiti da GetTempFileName(), idealmente in un blocco finally).
BrainSlugs83,

Risposte:


355

Garantito per essere (statisticamente) unico:

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

(Per citare l'articolo della wiki sulla probabilità di una collisione:

... si stima che il rischio annuale di essere colpiti da un meteorite sia una possibilità su 17 miliardi [19], il che significa che la probabilità è di circa 0,00000000006 (6 × 10−11), equivalente alla probabilità di creare alcune decine di trilioni di UUID in un anno e con un duplicato. In altre parole, solo dopo aver generato 1 miliardo di UUID al secondo per i prossimi 100 anni, la probabilità di creare un solo duplicato sarebbe di circa il 50%. La probabilità di un duplicato sarebbe di circa il 50% se ogni persona sulla terra possiede 600 milioni di UUID

EDIT: Vedi anche i commenti di JaredPar.


29
Ma non è garantito che si trovi in ​​una posizione scrivibile
JaredPar,

33
E sono non garantiti per essere unico nel suo genere a tutti, solo statisticamente improbabile.
paxdiablo,

30
@Pax: hai più soldi per vincere la lotteria 1000 volte di seguito che per generare due guide ideiche. È abbastanza unico, immagino ...
Mitch Wheat,

11
@Mitch il motivo per cui non è univoco è perché è possibile per me semplicemente creare un file con lo stesso nome nello stesso percorso. Anche se i GUID sono garantiti come unici sono anche prevedibili, il che significa che, dati abbastanza informazioni, potrei indovinare la prossima serie di guide generate dalla tua scatola
JaredPar,

207
brava gente, cercate di tenere la testa fuori dalle nuvole. L'approccio è: generare un nome file casuale, quindi crearlo se non esiste. Quindi aiutalo a codificarlo bene. Tutto questo parlare di generatori pseudo-casuali e numeri universalmente unici è totalmente inutile.
Max Hodges,

58

Prova questa funzione ...

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

Restituirà un percorso completo con l'estensione di tua scelta.

Nota, non è garantito produrre un nome file univoco poiché qualcun altro potrebbe aver tecnicamente già creato quel file. Tuttavia, le probabilità che qualcuno indovini la guida successiva prodotta dalla tua app e la crei è molto bassa. È abbastanza sicuro supporre che questo sarà unico.


4
Forse Path.ChangeExtension () sarebbe più elegante di Guid.NewGuid (). ToString () + estensione
Ohad Schneider,

@ohadsc - anzi, Guid.NewGuid().ToString() + extensionnon è nemmeno corretto, dovrebbe essereGuid.NewGuid().ToString() + "." + extension
Stephen Swensen il

4
Suppongo che dipenda dal contratto del metodo (se si aspetta .txto txt), ma dal momento che ChangeExtensiongestisce entrambi i casi, non può far male
Ohad Schneider,

1
Chiamalo Suffix invece di Extension e tutti sono felici immagino
Chiel ten Brinke,

46
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: funziona come Path.GetTempFileName. Viene creato un file vuoto per riservare il nome del file. Effettua 10 tentativi, in caso di collisioni generate da Path.GetRandomFileName ();


Tenere presente che in Path.GetRandomFileName()realtà crea un file a zero byte sul disco e restituisce il percorso completo di quel file. Non stai usando questo file, solo il suo nome per cambiare l'estensione. Quindi, se non ti assicuri di eliminare questi file temporanei, dopo 65535 chiamate a questa funzione inizierà a fallire.
Vladimir Baranov,

7
Hai mescolato GetTempFileName()e GetRandomFileName(). GetTempFileName()crea un file a zero byte come il mio metodo, ma GetRandomFileName()non crea un file. Dai documenti:> A differenza di GetTempFileName, GetRandomFileName non crea un file. Il tuo link punta alla pagina sbagliata.
Maxence,

19

In alternativa, puoi anche utilizzare 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");

Qui ho usato un'estensione txt ma puoi specificare quello che vuoi. Ho anche impostato il flag keep su true in modo che il file temporaneo sia conservato dopo l'uso. Sfortunatamente, TempFileCollection crea un file casuale per estensione. Se hai bisogno di più file temporanei, puoi creare più istanze di TempFileCollection.


10

Perché non controllare se il file esiste?

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

9
File.Exists ti dice informazioni sul passato e quindi non è affidabile. Tra la restituzione di File.Exist e l'esecuzione del codice è possibile che il file venga creato.
JaredPar,

4
... allora sei al sicuro con il tuo programma forse ma non con altri processi che scrivono un file su quella stessa identica destinazione ...
Koen,

@JaredPar e qual è la propensione di questo accada?
Migol,

2
@Migol È molto basso e per definizione una condizione eccezionale. Hmmm, esattamente per quali eccezioni sono state progettate per essere utilizzate.
Cody Grey

3
La possibilità di @CodyGray per lo scontro guid è 1/2 ^ 128. La probabilità che ciò accada 2 volte è 1/2 ^ 256 ecc. Non preoccuparti!
Migol,

9

La documentazione MSDN per GetTempFileName di C ++ discute le tue preoccupazioni e le risponde:

GetTempFileName non è in grado di garantire che il nome del file sia univoco .

Vengono utilizzati solo i 16 bit inferiori del parametro uUnique. Ciò limita GetTempFileName a un massimo di 65.535 nomi di file univoci se i parametri lpPathName e lpPrefixString rimangono invariati.

A causa dell'algoritmo utilizzato per generare i nomi dei file, GetTempFileName può funzionare male quando si crea un numero elevato di file con lo stesso prefisso. In questi casi, si consiglia di costruire nomi di file univoci basati su GUID .


2
Il metodo GetTempFileName genererà una IOException se viene utilizzato per creare più di 65535 file senza eliminare i file temporanei precedenti. Il metodo GetTempFileName genererà una IOException se non è disponibile un nome file temporaneo univoco. Per risolvere questo errore, eliminare tutti i file temporanei non necessari.
Max Hodges,

Questa è una citazione incompleta. La citazione pertinente: " Se uUnique non è zero , è necessario creare il file da soli. Viene creato solo un nome file, poiché GetTempFileName non è in grado di garantire che il nome file sia univoco." Se lo chiami nel modo in cui tutti discutono qui, uUnique sarà zero.
jnm2,

6

Che ne dite di:

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

È altamente improbabile che il computer generi lo stesso Guid nello stesso istante di tempo. L'unico punto debole che vedo qui è l'impatto sulle prestazioni che DateTime.Now.Ticks aggiungerà.


5

Puoi anche fare quanto segue

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

e questo funziona anche come previsto

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

2
questo fallirà se c'è un file temp.csv e crei temp.tmp e poi cambi l'estensione in csv
David

No, non ... GetTempFileName () crea un nome file univoco ... fino a un limite di 32 KB, a quel punto è necessario eliminare alcuni file, ma penso che la mia soluzione sia corretta. È sbagliato se dovessi passare un percorso di file in ChangeExtension che non è garantito per essere unico, ma non è quello che fa la mia soluzione.
Michael Prewecki,

11
GetTempFileName garantisce che il percorso che restituisce sarà univoco. Non che il percorso che restituisce + ".csv" sarà unico. Cambiare l'estensione in questo modo potrebbe fallire, come ha detto David.
Marcus Griep,

5
GetTempFileName crea un file, quindi il tuo primo esempio è una perdita di risorse.
Gary McGill,

3

A mio avviso, la maggior parte delle risposte proposte qui non è ottimale. Quello che si avvicina di più è quello originale proposto inizialmente da Brann.

Un nome file temporaneo deve essere

  • Unico
  • Senza conflitti (non esiste già)
  • Atomic (creazione di nome e file nella stessa operazione)
  • Difficile da indovinare

A causa di questi requisiti, non è una buona idea programmare una tale bestia da soli. Le persone intelligenti che scrivono librerie IO si preoccupano di cose come il blocco (se necessario) ecc. Pertanto, non vedo la necessità di riscrivere System.IO.Path.GetTempFileName ().

Questo, anche se sembra goffo, dovrebbe fare il lavoro:

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

2
Ma questo non è privo di conflitti, il "CSV" potrebbe già esistere ed essere sovrascritto.
xvan,

3
File.Movegenera IOExceptionse il file di destinazione esiste già. msdn.microsoft.com/en-us/library/...
joshuanapoli

2

Questo potrebbe essere utile per te ... È per creare una temperatura. cartella e restituirlo come stringa in VB.NET.

Facilmente convertibile in 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

2

Questo sembra funzionare bene per me: controlla l'esistenza del file e crea il file per essere sicuro che sia un percorso scrivibile. Dovrebbe funzionare bene, puoi cambiarlo per restituire direttamente FileStream (che normalmente è quello che ti serve per un file temporaneo):

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

2

Funzione facile in C #:

public static string GetTempFileName(string extension = "csv")
{
    return Path.ChangeExtension(Path.GetTempFileName(), extension);
}

GetTempFileName () crea un file temporaneo nella cartella temporanea. Di conseguenza, verranno creati due file temporanei, uno dei quali perde.
evgenybf,

1

Sulla base delle risposte che ho trovato da Internet, arrivo al mio codice come segue:

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

E come Cookbook C # di Jay Hilyard, Stephen Teilhet ha sottolineato nell'uso di un file temporaneo nell'applicazione :

  • è necessario utilizzare un file temporaneo ogni volta che è necessario archiviare temporaneamente le informazioni per il successivo recupero.

  • L'unica cosa che devi ricordare è eliminare questo file temporaneo prima che l'applicazione che lo ha creato sia terminata.

  • Se non viene eliminato, rimarrà nella directory temporanea dell'utente fino a quando l'utente non lo elimina manualmente.


1

Ho mescolato le risposte di @Maxence e @Mitch Wheat tenendo presente che desidero la semantica del metodo GetTempFileName (fileName è il nome di un nuovo file creato) che aggiunge l'estensione preferita.

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

0

Questo è quello che sto facendo:

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

Buono: include l'identificatore del processo Cattivo: non include l'identificatore del thread (sebbene sia possibile eseguirlo all'interno di un blocco) Cattivo: il timestamp ha una risoluzione di solo 1 secondo. In molte, molte applicazioni, è comune produrre molti file al secondo.
Andrewf,

0

Questo è un modo semplice ma efficace per generare nomi di file incrementali. Guarderà direttamente la corrente (puoi facilmente indicarla da qualche altra parte) e cercherà i file con la tua base YourApplicationName * .txt (di nuovo puoi facilmente cambiarla). Inizierà a 0000 in modo che il primo nome file sia YourApplicationName0000.txt. se per qualche motivo ci sono nomi di file con posta indesiderata tra (che significa non numeri) le parti sinistra e destra, tali file saranno ignorati in virtù della chiamata 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;
    }

-2

Penso che dovresti provare questo:

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

Genera un nome file univoco e crea un file con quel nome file in una posizione specificata.


3
Questa soluzione ha tanti problemi. non puoi combinare C: \ temp con un percorso assoluto, c: \ temp potrebbe non essere scrivibile e non garantisce che la versione .tmp del file sia unica.
Mark Lakata,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.