Comment puis-je créer un fichier temporaire avec une extension spécifique avec .NET?

277

J'ai besoin de générer un fichier temporaire unique avec une extension .csv.

Ce que je fais en ce moment c'est

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

Cependant, cela ne garantit pas que mon fichier .csv sera unique.

Je sais que les chances d'avoir une collision sont très faibles (surtout si vous considérez que je ne supprime pas les fichiers .tmp), mais ce code ne me semble pas bon.

Bien sûr, je pouvais générer manuellement des noms de fichiers aléatoires jusqu'à ce que j'en trouve finalement un unique (ce qui ne devrait pas être un problème), mais je suis curieux de savoir si d'autres ont trouvé une bonne façon de résoudre ce problème.

Brann
la source
4
quelques mises en garde à propos de GetTempFileName La méthode GetTempFileName lèvera une IOException si elle est utilisée pour créer plus de 65535 fichiers sans supprimer les fichiers temporaires précédents. La méthode GetTempFileName déclenchera une IOException si aucun nom de fichier temporaire unique n'est disponible. Pour résoudre cette erreur, supprimez tous les fichiers temporaires inutiles.
Max Hodges
2
Les fichiers temporaires sont principalement utilisés pour un ensemble spécifique de conditions. Si l'extension de fichier est importante, je me demande si l'utilisation de GetTempFileName n'est pas la solution d'écriture. Je sais que cela fait longtemps, mais si vous nous en disiez plus sur le contexte et la nécessité de ces fichiers, nous pourrions peut-être proposer une meilleure approche. plus ici: support.microsoft.com/kb/92635?wa=wsignin1.0
Max Hodges
1
Gardez à l'esprit GetTempFileName() crée un nouveau fichier chaque fois que vous l'appelez. - Si vous changez immédiatement la chaîne en quelque chose d'autre, vous venez de créer un nouveau fichier zéro octet dans votre répertoire temporaire (et comme d'autres l'ont remarqué, cela finira par entraîner son échec lorsque vous frapperez 65535 fichiers là-dedans ...) - - Pour éviter cela, assurez-vous de supprimer tous les fichiers que vous créez dans ce dossier (y compris ceux renvoyés par GetTempFileName(), idéalement dans un bloc enfin).
BrainSlugs83

Réponses:

356

Garanti (statistiquement) unique:

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

(Pour citer un article du wiki sur la probabilité d'une collision:

... le risque annuel d'être touché par une météorite est estimé à une chance sur 17 milliards [19], ce qui signifie que la probabilité est d'environ 0,0000000000006 (6 × 10−11), ce qui équivaut à la probabilité de créer quelques dizaines de mille milliards d'UUID en un an et en avoir un double. En d'autres termes, seulement après avoir généré 1 milliard d'UUID par seconde pendant les 100 prochaines années, la probabilité de créer un seul doublon serait d'environ 50%. La probabilité d'un double serait d'environ 50% si chaque personne sur terre possède 600 millions d'UUID

EDIT: Veuillez également consulter les commentaires de JaredPar.

Blé Mitch
la source
29
Mais pas garanti d'être dans un emplacement accessible en écriture
JaredPar
33
Et ils ne sont pas du tout garantis uniques, mais statistiquement peu probables.
paxdiablo
30
@Pax: vous avez plus de chance de gagner à la loterie 1000 fois de suite que de générer deux guides idéologiques. C'est assez unique, je suppose ...
Mitch Wheat
11
@Mitch la raison pour laquelle ce n'est pas unique est parce qu'il m'est possible de créer simplement un fichier avec le même nom dans le même chemin. Les GUID, bien que garantis d'être uniques, sont également prévisibles, ce qui signifie que si je dispose de suffisamment d'informations, je pourrais deviner le prochain ensemble de guides générés par votre boîte
JaredPar
207
bons seigneurs, essayez plus fort de garder la tête hors des nuages. L'approche est la suivante: générer un nom de fichier aléatoire, puis le créer s'il n'existe pas. Alors, aidez-le simplement à bien coder. Tout ce discours sur les générateurs pseudo-aléatoires et les nombres universellement uniques est totalement inutile.
Max Hodges
58

Essayez cette fonction ...

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

Il renverra un chemin complet avec l'extension de votre choix.

Remarque, il n'est pas garanti de produire un nom de fichier unique car quelqu'un d'autre aurait pu techniquement déjà créé ce fichier. Cependant, les chances que quelqu'un devine le prochain guide produit par votre application et le crée sont très très faibles. Il est assez sûr de supposer que ce sera unique.

JaredPar
la source
4
Path.ChangeExtension () serait peut-être plus élégant que Guid.NewGuid (). ToString () + extension
Ohad Schneider
@ohadsc - en effet, Guid.NewGuid().ToString() + extensionn'est même pas correct, il devrait l'êtreGuid.NewGuid().ToString() + "." + extension
Stephen Swensen
4
Je suppose que cela dépend du contrat de la méthode (que ce soit prévu .txtou non txt) mais comme il ChangeExtensiongère les deux cas, cela ne peut pas faire de mal
Ohad Schneider
1
Appelez-le Suffixe au lieu d'Extension et tout le monde est content, je suppose
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);
    }
  }
}

Remarque: cela fonctionne comme Path.GetTempFileName. Un fichier vide est créé pour réserver le nom du fichier. Il fait 10 tentatives, en cas de collisions générées par Path.GetRandomFileName ();

Maxence
la source
N'oubliez pas que cela Path.GetRandomFileName()crée en fait un fichier de zéro octet sur le disque et renvoie le chemin complet de ce fichier. Vous n'utilisez pas ce fichier, seulement son nom pour changer l'extension. Donc, si vous ne vous assurez pas de supprimer ces fichiers temporaires, après 65535 appels à cette fonction, il commencera à échouer.
Vladimir Baranov
7
Vous avez mélangé GetTempFileName()et GetRandomFileName(). GetTempFileName()créer un fichier zéro octet comme ma méthode, mais GetRandomFileName()ne crée pas de fichier. À partir des documents:> Contrairement à GetTempFileName, GetRandomFileName ne crée pas de fichier. Votre lien pointe vers la mauvaise page.
Maxence
19

Vous pouvez également utiliser 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");

Ici, j'ai utilisé une extension txt mais vous pouvez spécifier ce que vous voulez. J'ai également défini l'indicateur keep sur true afin que le fichier temporaire soit conservé après utilisation. Malheureusement, TempFileCollection crée un fichier aléatoire par extension. Si vous avez besoin de plus de fichiers temporaires, vous pouvez créer plusieurs instances de TempFileCollection.

Mehmet Aras
la source
10

Pourquoi ne pas vérifier si le fichier existe?

string fileName;
do
{
    fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
} while (System.IO.File.Exists(fileName));
Matías
la source
9
File.Exists vous donne des informations sur le passé et n'est donc pas fiable. Entre le retour de File.Exist et l'exécution de votre code, il est possible que le fichier soit créé.
JaredPar
4
... alors vous êtes en sécurité avec votre propre programme peut-être mais pas avec un autre processus écrivant un fichier sur cette même destination exacte ...
Koen
@JaredPar et quelle est la probabilité que cela se produise?
Migol
2
@Migol C'est très bas, et par définition une condition exceptionnelle. Hmmm, exactement à quelles fins les exceptions ont été conçues.
Cody Gray
3
@CodyGray a une chance pour le choc de guid de 1/2 ^ 128. La chance que cela se produise 2 fois est 1/2 ^ 256 etc. Ne vous embêtez pas!
Migol
10

La documentation MSDN pour GetTempFileName de C ++ explique votre problème et y répond:

GetTempFileName ne peut pas garantir que le nom de fichier est unique .

Seuls les 16 bits inférieurs du paramètre uUnique sont utilisés. Cela limite GetTempFileName à un maximum de 65 535 noms de fichiers uniques si les paramètres lpPathName et lpPrefixString restent les mêmes.

En raison de l'algorithme utilisé pour générer les noms de fichiers, GetTempFileName peut mal fonctionner lors de la création d'un grand nombre de fichiers avec le même préfixe. Dans de tels cas, il est recommandé de créer des noms de fichiers uniques en fonction des GUID .

Colonel Panic
la source
2
La méthode GetTempFileName déclenchera une IOException si elle est utilisée pour créer plus de 65535 fichiers sans supprimer les fichiers temporaires précédents. La méthode GetTempFileName déclenchera une IOException si aucun nom de fichier temporaire unique n'est disponible. Pour résoudre cette erreur, supprimez tous les fichiers temporaires inutiles.
Max Hodges
C'est une citation incomplète. La citation pertinente: " Si uUnique n'est pas nul , vous devez créer le fichier vous-même. Seul un nom de fichier est créé, car GetTempFileName n'est pas en mesure de garantir que le nom de fichier est unique." Si vous l'appelez comme tout le monde en parle ici, uUnique sera nul.
jnm2
6

Que diriez-vous:

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

Il est hautement improbable que l'ordinateur génère le même Guid au même instant. La seule faiblesse que je vois ici est l'impact sur les performances que DateTime.Now.Ticks ajoutera.

IRBaboon
la source
5

Vous pouvez également effectuer les opérations suivantes

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

et cela fonctionne aussi comme prévu

string filename = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv");
Michael Prewecki
la source
2
cela échouera s'il existe un fichier temp.csv et que vous créez temp.tmp puis changez l'extension en csv
David
Non, ce ne sera pas le cas ... GetTempFileName () crée un nom de fichier unique ... jusqu'à une certaine limite de 32 Ko, moment auquel vous devez supprimer certains fichiers mais je pense que ma solution est correcte. C'est faux si je devais passer un chemin de fichier dans ChangeExtension qui n'est pas garanti d'être unique, mais ce n'est pas ce que fait ma solution.
Michael Prewecki
11
GetTempFileName garantit que le chemin qu'il renvoie sera unique. Ce n'est pas que le chemin qu'il renvoie + ".csv" sera unique. Changer l'extension de cette manière pourrait échouer comme l'a dit David.
Marcus Griep
5
GetTempFileName crée un fichier, donc votre premier exemple est une fuite de ressources.
Gary McGill
3

À mon avis, la plupart des réponses proposées ici ne sont pas optimales. Celui qui s'en rapproche le plus est celui proposé initialement par Brann.

Un nom de fichier temporaire doit être

  • Unique
  • Sans conflit (n'existe pas déjà)
  • Atomic (Création du nom et du fichier dans la même opération)
  • Difficile à deviner

En raison de ces exigences, ce n'est pas une mauvaise idée de programmer une telle bête par vous-même. Les personnes intelligentes écrivant des bibliothèques d'E / S s'inquiètent de choses comme le verrouillage (si nécessaire), etc. Par conséquent, je ne vois pas la nécessité de réécrire System.IO.Path.GetTempFileName ().

Cela, même s'il semble maladroit, devrait faire l'affaire:

//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
la source
2
Mais ce n'est pas sans conflit, le «csv» pourrait déjà exister et être écrasé.
xvan
3
File.Moveaugmente IOExceptionsi le fichier de destination existe déjà. msdn.microsoft.com/en-us/library/…
joshuanapoli
2

Cela pourrait être pratique pour vous ... C'est pour créer un temp. dossier et le renvoyer sous forme de chaîne dans VB.NET.

Facilement convertible en 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
la source
2

Cela semble bien fonctionner pour moi: il vérifie l'existence du fichier et crée le fichier pour être sûr qu'il s'agit d'un emplacement accessible en écriture. Devrait fonctionner correctement, vous pouvez le changer pour retourner directement le FileStream (qui est normalement ce dont vous avez besoin pour un fichier temporaire):

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
la source
2

Fonction facile en C #:

public static string GetTempFileName(string extension = "csv")
{
    return Path.ChangeExtension(Path.GetTempFileName(), extension);
}
Ranch Camal
la source
GetTempFileName () crée un fichier temporaire dans le dossier temporaire. En conséquence, vous aurez créé deux fichiers temporaires, dont l'un fuira.
evgenybf
1

Sur la base des réponses que j'ai trouvées sur Internet, j'arrive à mon code comme suit:

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

Et comme le livre de recettes C # par Jay Hilyard, Stephen Teilhet a souligné dans Utilisation d'un fichier temporaire dans votre application :

  • vous devez utiliser un fichier temporaire chaque fois que vous avez besoin de stocker des informations temporairement pour une récupération ultérieure.

  • La seule chose dont vous devez vous souvenir est de supprimer ce fichier temporaire avant la fermeture de l'application qui l'a créé.

  • S'il n'est pas supprimé, il restera dans le répertoire temporaire de l'utilisateur jusqu'à ce que l'utilisateur le supprime manuellement.

Speedoops
la source
1

J'ai mélangé les réponses @Maxence et @Mitch Wheat en gardant à l'esprit que je veux la sémantique de la méthode GetTempFileName (le fileName est le nom d'un nouveau fichier créé) en ajoutant l'extension préférée.

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
la source
0

Voici ce que je fais:

string tStamp = String.Format ("{0: aaaaMMjj.HHmmss}", DateTime.Now);
chaîne ProcID = Process.GetCurrentProcess (). Id.ToString ();
string tmpFolder = System.IO.Path.GetTempPath ();
string outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";
Michael Fitzpatrick
la source
Bon: inclut l'identifiant du processus Mauvais: n'inclut pas l'identifiant du thread (bien que vous puissiez l'exécuter à l'intérieur d'un verrou) Mauvais: l'horodatage n'a qu'une résolution de 1 seconde. Dans de nombreuses applications, il est courant de produire plusieurs fichiers par seconde.
andrewf
0

Il s'agit d'un moyen simple mais efficace de générer des noms de fichiers incrémentiels. Il cherchera directement dans le courant (vous pouvez facilement le signaler ailleurs) et recherchera des fichiers avec la base YourApplicationName * .txt (encore une fois, vous pouvez facilement changer cela). Il commencera à 0000 afin que le premier nom de fichier soit YourApplicationName0000.txt. si pour une raison quelconque, il y a des noms de fichiers avec des ordures entre (ce qui ne signifie pas des nombres) les parties gauche et droite, ces fichiers seront ignorés en raison de l'appel 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;
    }
Afficher un nom
la source
-2

Je pense que vous devriez essayer ceci:

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

Il génère un nom de fichier unique et crée un fichier avec ce nom de fichier à un emplacement spécifié.

Smita
la source
3
Cette solution a tellement de problèmes avec elle. vous ne pouvez pas combiner C: \ temp avec un chemin absolu, c: \ temp peut ne pas être accessible en écriture et cela ne garantit pas que la version .tmp du fichier est unique.
Mark Lakata