Wie kann ich mit .NET eine temporäre Datei mit einer bestimmten Erweiterung erstellen?

277

Ich muss eine eindeutige temporäre Datei mit der Erweiterung .csv generieren.

Was ich gerade mache, ist

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

Dies garantiert jedoch nicht, dass meine CSV-Datei eindeutig ist.

Ich weiß, dass die Wahrscheinlichkeit, dass ich jemals eine Kollision habe, sehr gering ist (insbesondere wenn Sie bedenken, dass ich die .tmp-Dateien nicht lösche), aber dieser Code sieht für mich nicht gut aus.

Natürlich könnte ich manuell zufällige Dateinamen generieren, bis ich schließlich einen eindeutigen finde (was kein Problem sein sollte), aber ich bin gespannt, ob andere einen guten Weg gefunden haben, um mit diesem Problem umzugehen.

Brann
quelle
4
Einige Einschränkungen zu GetTempFileName Die GetTempFileName-Methode löst eine IOException aus, wenn damit mehr als 65535 Dateien erstellt werden, ohne vorherige temporäre Dateien zu löschen. Die GetTempFileName-Methode löst eine IOException aus, wenn kein eindeutiger temporärer Dateiname verfügbar ist. Löschen Sie alle nicht benötigten temporären Dateien, um diesen Fehler zu beheben.
Max Hodges
2
Temporäre Dateien werden hauptsächlich für bestimmte Bedingungen verwendet. Wenn die Dateierweiterung wichtig ist, frage ich mich, ob die Verwendung von GetTempFileName möglicherweise nicht die Schreiblösung ist. Ich weiß, es ist lange her, aber wenn Sie uns mehr über den Kontext und die Notwendigkeit dieser Dateien erzählen, können wir möglicherweise einen besseren Ansatz vorschlagen. mehr hier: support.microsoft.com/kb/92635?wa=wsignin1.0
Max Hodges
1
Denken Sie daran, dass bei jedem Aufruf eine neue Datei GetTempFileName() erstellt wird . - Wenn Sie die Zeichenfolge sofort in eine andere ändern, haben Sie gerade eine neue Null-Byte-Datei in Ihrem temporären Verzeichnis erstellt (und wie andere angemerkt haben, schlägt dies möglicherweise fehl, wenn Sie dort 65535-Dateien aufrufen ...) - - Um dies zu vermeiden, löschen Sie alle Dateien, die Sie in diesem Ordner erstellen (einschließlich der von, die von zurückgegeben werden GetTempFileName(), idealerweise in einem finally-Block).
BrainSlugs83

Antworten:

354

Garantiert (statistisch) eindeutig:

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

(Um aus dem Wiki-Artikel über die Wahrscheinlichkeit einer Kollision zu zitieren:

... das jährliche Risiko, von einem Meteoriten getroffen zu werden, wird auf eine Chance von 17 Milliarden geschätzt [19], was bedeutet, dass die Wahrscheinlichkeit etwa 0,00000000006 (6 × 10−11) beträgt, was der Wahrscheinlichkeit entspricht, einige zehn zu erzeugen Billionen von UUIDs in einem Jahr und mit einem Duplikat. Mit anderen Worten, erst nachdem in den nächsten 100 Jahren pro Sekunde 1 Milliarde UUIDs generiert wurden, würde die Wahrscheinlichkeit, nur ein Duplikat zu erstellen, bei etwa 50% liegen. Die Wahrscheinlichkeit eines Duplikats würde etwa 50% betragen, wenn jeder Mensch auf der Erde 600 Millionen UUIDs besitzt

EDIT: Bitte beachten Sie auch die Kommentare von JaredPar.

Mitch Wheat
quelle
29
Aber nicht garantiert an einem beschreibbaren Ort
JaredPar
33
Und es ist nicht garantiert, dass sie überhaupt einzigartig sind, nur statistisch unwahrscheinlich.
Paxdiablo
30
@Pax: Sie haben mehr Chancen, 1000 Mal hintereinander im Lotto zu gewinnen, als zwei ideelle Leitfäden zu generieren. Das ist einzigartig genug, denke ich ...
Mitch Wheat
11
@Mitch Der Grund, warum es nicht eindeutig ist, ist , dass ich einfach eine Datei mit demselben Namen im selben Pfad erstellen kann. GUIDs sind zwar garantiert eindeutig, aber auch vorhersehbar, was bedeutet, dass ich bei genügend Informationen die nächsten von Ihrer Box generierten
Guids
207
Guter Herr, bemüht euch, euren Kopf von den Wolken fernzuhalten. Der Ansatz lautet: Generieren Sie einen zufälligen Dateinamen und erstellen Sie ihn, falls er nicht vorhanden ist. Also hilf ihm einfach, das gut zu codieren. All dieses Gerede über Pseudozufallsgeneratoren und universell eindeutige Zahlen ist völlig unnötig.
Max Hodges
58

Probieren Sie diese Funktion aus ...

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

Es wird ein vollständiger Pfad mit der Erweiterung Ihrer Wahl zurückgegeben.

Es ist nicht garantiert, dass ein eindeutiger Dateiname erstellt wird, da diese Datei möglicherweise bereits von einer anderen Person erstellt wurde. Die Wahrscheinlichkeit, dass jemand die nächste von Ihrer App erstellte Anleitung errät und erstellt, ist jedoch sehr gering. Es ist ziemlich sicher anzunehmen, dass dies einzigartig sein wird.

JaredPar
quelle
4
Vielleicht wäre Path.ChangeExtension () eleganter als Guid.NewGuid (). ToString () + Erweiterung
Ohad Schneider
@ohadsc - in der Tat Guid.NewGuid().ToString() + extensionist nicht einmal richtig, sollte es seinGuid.NewGuid().ToString() + "." + extension
Stephen Swensen
4
Ich nehme an, das hängt vom Vertrag der Methode ab (ob erwartet .txtoder nicht txt), aber da ChangeExtensionbeide Fälle behandelt werden, kann es nicht schaden
Ohad Schneider
1
Nennen Sie es Suffix statt Erweiterung und jeder ist glücklich, denke ich
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);
    }
  }
}

Hinweis: Dies funktioniert wie Path.GetTempFileName. Eine leere Datei wird erstellt, um den Dateinamen zu reservieren. Bei Kollisionen, die von Path.GetRandomFileName () generiert werden, werden 10 Versuche ausgeführt.

Maxence
quelle
Beachten Sie, dass Path.GetRandomFileName()tatsächlich eine Null-Byte-Datei auf der Festplatte erstellt wird und der vollständige Pfad dieser Datei zurückgegeben wird. Sie verwenden diese Datei nicht, sondern nur ihren Namen, um die Erweiterung zu ändern. Wenn Sie also nicht sicherstellen, dass Sie diese temporären Dateien löschen, schlägt sie nach 65535-Aufrufen dieser Funktion fehl.
Vladimir Baranov
7
Du hast gemischt GetTempFileName()und GetRandomFileName(). GetTempFileName()Erstellen Sie eine Null-Byte-Datei wie meine Methode, GetRandomFileName()erstellen Sie jedoch keine Datei. Aus den Dokumenten:> Im Gegensatz zu GetTempFileName erstellt GetRandomFileName keine Datei. Ihr Link verweist auf die falsche Seite.
Maxence
19

Alternativ können Sie auch System.CodeDom.Compiler.TempFileCollection verwenden .

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

Hier habe ich eine txt-Erweiterung verwendet, aber Sie können angeben, was Sie wollen. Ich habe auch das Keep-Flag auf true gesetzt, damit die temporäre Datei nach der Verwendung erhalten bleibt. Leider erstellt TempFileCollection eine zufällige Datei pro Erweiterung. Wenn Sie mehr temporäre Dateien benötigen, können Sie mehrere Instanzen von TempFileCollection erstellen.

Mehmet Aras
quelle
10

Warum nicht prüfen, ob die Datei existiert?

string fileName;
do
{
    fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
} while (System.IO.File.Exists(fileName));
Matías
quelle
9
File.Exists informiert Sie über die Vergangenheit und ist daher nicht zuverlässig. Zwischen der Rückgabe von File.Exist und der Ausführung Ihres Codes kann die Datei erstellt werden.
JaredPar
4
... dann sind Sie vielleicht mit Ihrem eigenen Programm sicher, aber nicht mit einem anderen Prozess, der eine Datei an genau demselben Ziel schreibt ...
Koen
@JaredPar und wie ist die Wahrscheinlichkeit dafür?
Migol
2
@ Migol Es ist sehr niedrig und per Definition ein außergewöhnlicher Zustand. Hmmm, genau für welche Ausnahmen wurde entwickelt?
Cody Gray
3
@CodyGray Chance für Guid Clash ist 1/2 ^ 128. Die Wahrscheinlichkeit, dass es 2 Mal passieren würde, ist 1/2 ^ 256 usw. Mach dir keine Sorgen!
Migol
9

In der MSDN-Dokumentation zu GetTempFileName von C ++ wird Ihr Anliegen erläutert und beantwortet:

GetTempFileName kann nicht garantieren, dass der Dateiname eindeutig ist .

Es werden nur die unteren 16 Bits des Parameters uUnique verwendet. Dies begrenzt GetTempFileName auf maximal 65.535 eindeutige Dateinamen, wenn die Parameter lpPathName und lpPrefixString gleich bleiben.

Aufgrund des zum Generieren von Dateinamen verwendeten Algorithmus kann GetTempFileName beim Erstellen einer großen Anzahl von Dateien mit demselben Präfix eine schlechte Leistung erzielen. In solchen Fällen wird empfohlen, eindeutige Dateinamen basierend auf GUIDs zu erstellen .

Oberst Panik
quelle
2
Die GetTempFileName-Methode löst eine IOException aus, wenn damit mehr als 65535 Dateien erstellt werden, ohne vorherige temporäre Dateien zu löschen. Die GetTempFileName-Methode löst eine IOException aus, wenn kein eindeutiger temporärer Dateiname verfügbar ist. Löschen Sie alle nicht benötigten temporären Dateien, um diesen Fehler zu beheben.
Max Hodges
Das ist ein unvollständiges Zitat. Das relevante Zitat: " Wenn uUnique nicht Null ist , müssen Sie die Datei selbst erstellen. Es wird nur ein Dateiname erstellt, da GetTempFileName nicht garantieren kann, dass der Dateiname eindeutig ist." Wenn Sie es so nennen, wie alle hier diskutieren, ist uUnique Null.
jnm2
6

Wie wäre es mit:

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

Es ist höchst unwahrscheinlich, dass der Computer zum gleichen Zeitpunkt dieselbe Guid generiert. Die einzige Schwäche, die ich hier sehe, ist die Auswirkung auf die Leistung, die DateTime.Now.Ticks hinzufügen wird.

IRBaboon
quelle
5

Sie können auch Folgendes tun

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

und das funktioniert auch wie erwartet

string filename = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv");
Michael Prewecki
quelle
2
Dies wird fehlschlagen, wenn es eine Datei temp.csv gibt und Sie temp.tmp erstellen und dann die Erweiterung in csv ändern
David
Nein, wird es nicht ... GetTempFileName () erstellt einen eindeutigen Dateinamen ... bis zu einer Grenze von 32 KB. Zu diesem Zeitpunkt müssen Sie einige Dateien löschen, aber ich denke, meine Lösung ist korrekt. Es ist falsch, wenn ich einen Dateipfad an ChangeExtension übergebe, der nicht garantiert eindeutig ist, aber das ist nicht das, was meine Lösung tut.
Michael Prewecki
11
GetTempFileName garantiert, dass der zurückgegebene Pfad eindeutig ist. Nicht, dass der zurückgegebene Pfad + ".csv" eindeutig wäre. Das Ändern der Erweiterung auf diese Weise könnte fehlschlagen, wie David sagte.
Marcus Griep
5
GetTempFileName erstellt eine Datei, sodass Ihr erstes Beispiel ein Ressourcenleck ist.
Gary McGill
3

Meiner Meinung nach werden die meisten Antworten hier als nicht optimal vorgeschlagen. Derjenige, der am nächsten kommt, ist der ursprüngliche, der ursprünglich von Brann vorgeschlagen wurde.

Ein temporärer Dateiname muss sein

  • Einzigartig
  • Konfliktfrei (noch nicht vorhanden)
  • Atomic (Erstellung von Name & Datei im selben Vorgang)
  • Schwer zu erraten

Aufgrund dieser Anforderungen ist es keine gute Idee, ein solches Tier selbst zu programmieren. Intelligente Leute, die E / A-Bibliotheken schreiben, sorgen sich um Dinge wie Sperren (falls erforderlich) usw. Daher sehe ich keine Notwendigkeit, System.IO.Path.GetTempFileName () neu zu schreiben.

Dies sollte, auch wenn es ungeschickt aussieht, den Job machen:

//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);
Daniel C. Oderbolz
quelle
2
Aber das ist nicht konfliktfrei, die 'csv' könnte bereits existieren und überschrieben werden.
Xvan
3
File.Movewird ausgelöst, IOExceptionwenn die Zieldatei bereits vorhanden ist. msdn.microsoft.com/en-us/library/…
joshuanapoli
2

Dies könnte für Sie nützlich sein ... Es ist eine temporäre zu erstellen. Ordner und geben Sie es als Zeichenfolge in VB.NET zurück.

Einfach in C # umwandelbar:

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
quelle
2

Dies scheint für mich in Ordnung zu sein: Es prüft, ob Dateien vorhanden sind, und erstellt die Datei, um sicherzustellen, dass es sich um einen beschreibbaren Speicherort handelt. Sollte dies gut funktionieren, können Sie es ändern, um direkt den FileStream zurückzugeben (was normalerweise für eine temporäre Datei erforderlich ist):

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
quelle
2

Einfache Funktion in C #:

public static string GetTempFileName(string extension = "csv")
{
    return Path.ChangeExtension(Path.GetTempFileName(), extension);
}
Ranch Camal
quelle
GetTempFileName () erstellt eine temporäre Datei im temporären Ordner. Als Ergebnis werden zwei temporäre Dateien erstellt, von denen eine ausläuft.
evgenybf vor
1

Ich habe die Antworten @Maxence und @Mitch Wheat gemischt, wobei ich die Semantik der GetTempFileName-Methode (der Dateiname ist der Name einer neu erstellten Datei) verwenden möchte, wobei die bevorzugte Erweiterung hinzugefügt wird.

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
quelle
0

Das mache ich:

string tStamp = String.Format ("{0: yyyyMMdd.HHmmss}", DateTime.Now);
Zeichenfolge ProcID = Process.GetCurrentProcess (). Id.ToString ();
Zeichenfolge tmpFolder = System.IO.Path.GetTempPath ();
Zeichenfolge outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";
Michael Fitzpatrick
quelle
Gut: Prozesskennung enthält Schlecht: Enthält keine Thread-ID (obwohl Sie diese in einer Sperre ausführen könnten) Schlecht: Der Zeitstempel hat nur eine Auflösung von 1 Sekunde. In vielen, vielen Anwendungen ist es üblich, viele Dateien pro Sekunde zu erstellen.
Andrewf
0

Dies ist eine einfache, aber effektive Methode, um inkrementelle Dateinamen zu generieren. Es wird direkt in der aktuellen Version angezeigt (Sie können dies leicht auf eine andere Stelle verweisen) und nach Dateien mit der Basis YourApplicationName * .txt suchen (auch dies können Sie problemlos ändern). Es beginnt um 0000, sodass der erste Dateiname YourApplicationName0000.txt lautet. Wenn sich aus irgendeinem Grund Dateinamen mit Junk zwischen dem linken und dem rechten Teil befinden (dh keine Zahlen), werden diese Dateien aufgrund des Tryparse-Aufrufs ignoriert.

    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;
    }
Anzeigename
quelle
0

Aufgrund der Antworten, die ich aus dem Internet gefunden habe, komme ich wie folgt zu meinem Code:

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

Und als C # -Kochbuch von Jay Hilyard wies Stephen Teilhet auf die Verwendung einer temporären Datei in Ihrer Anwendung hin :

  • Sie sollten eine temporäre Datei verwenden, wenn Sie Informationen vorübergehend speichern müssen, um sie später abzurufen.

  • Das einzige, woran Sie denken müssen, ist, diese temporäre Datei zu löschen, bevor die Anwendung, die sie erstellt hat, beendet wird.

  • Wenn es nicht gelöscht wird, bleibt es im temporären Verzeichnis des Benutzers, bis der Benutzer es manuell löscht.

Speedoops
quelle
-2

Ich denke, Sie sollten dies versuchen:

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

Es generiert einen eindeutigen Dateinamen und erstellt eine Datei mit diesem Dateinamen an einem bestimmten Speicherort.

Smita
quelle
3
Diese Lösung hat so viele Probleme. Sie können C: \ temp nicht mit einem absoluten Pfad kombinieren. C: \ temp ist möglicherweise nicht beschreibbar und garantiert nicht, dass die .tmp-Version der Datei eindeutig ist.
Mark Lakata