142 votes

Comment générer des noms de fichiers uniques en C#

J'ai implémenté un algorithme qui générera des noms uniques pour les fichiers qui seront enregistrés sur le disque dur. J'ajoute DateTime : Heures,Minutes,Secondes et Millisecondes mais cela génère encore des noms de fichiers en double car j'envoie plusieurs fichiers en même temps.

Quelle est la meilleure solution pour générer des noms uniques pour les fichiers à stocker sur le disque dur afin qu'aucun fichier ne soit identique?

0 votes

Cela dépend des autres exigences; cette [ancienne] question était / est trop vague.

256voto

Uwe Keim Points 15221

Si la lisibilité n'est pas importante, utilisez GUIDs.

Par exemple :

var monNomUniqueDeFichier = string.Format(@"{0}.txt", Guid.NewGuid());

ou plus court :

var monNomUniqueDeFichier = $@"{Guid.NewGuid()}.txt";

Dans mes programmes, j'essaie parfois par exemple 10 fois de générer un nom lisible ("Image1.png"…"Image10.png") et si cela échoue (parce que le fichier existe déjà), je reviens aux GUIDs.

Mise à jour :

Récemment, j'utilise également DateTime.Now.Ticks au lieu de GUIDs :

var monNomUniqueDeFichier = string.Format(@"{0}.txt", DateTime.Now.Ticks);

ou

var monNomUniqueDeFichier = $@"{DateTime.Now.Ticks}.txt";

L'avantage pour moi est que cela génère un nom de fichier plus court et plus "agréable à regarder" par rapport aux GUIDs.

Veuillez noter que dans certains cas (par exemple lorsque vous générez beaucoup de noms aléatoires en très peu de temps), cela peut générer des valeurs non uniques.

Stick to GUIDs if you want to make really sure that the file names are unique, even when transferring them to other computers.

9 votes

J'aime utiliser les Ticks comme un GUID est vraiment moche. Vous pouvez également obtenir un hachage des Ticks qui réduit la longueur des caractères du nom de fichier. DateTime.Now.Ticks.GetHashCode().ToString("x").ToUpper()

7 votes

"Ticks" est prévisible et non sécurisé par rapport aux threads (car les mêmes 'ticks' peuvent être obtenus à partir de plusieurs threads/processus). Cela le rend inadapté à la génération de noms de fichiers temporaires. Générer X..1..N peut être adapté aux tâches orientées utilisateur (c'est-à-dire copier dans l'Explorateur), mais est douteux pour le travail en serveur.

93voto

KBBWrite Points 1448

Utilisez

Path.GetTempFileName()

ou utilisez new GUID().

Path.GetTempFilename() sur MSDN.

1 votes

Voici le lien vers la documentation MSDN : msdn.microsoft.com/en-us/library/…

3 votes

Notez cependant que GetTempFileName() pourrait jeter une exception si vous créez de nombreux fichiers de ce type sans les supprimer.

24 votes

Le 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.

82voto

Rami Shareef Points 2901
System.IO.Path.GetRandomFileName()

Path.GetRandomFileName() sur MSDN.

1 votes

Voici le lien vers la documentation MSDN : msdn.microsoft.com/en-us/library/…

13 votes

@RyBolt: Puisque vous n'avez pas à vous en charger, il n'y a pratiquement rien que vous devez garder à l'esprit pour utiliser cette méthode. Et je m'attends à ce que la grande majorité des développeurs n'aient aucune idée de comment construire des systèmes cryptographiques sécurisés.

55voto

Mas Points 1529

Si la lisibilité du nom de fichier n'est pas importante, alors le GUID, comme suggéré par beaucoup, fera l'affaire. Cependant, je trouve qu'examiner un répertoire avec 1000 noms de fichiers GUID est très intimidant pour s'y retrouver. Je vais donc généralement utiliser une combinaison d'une chaîne statique qui fournit des informations de contexte sur le nom de fichier, une horodatage et un GUID.

Par exemple :

public string GenererNomFichier(string contexte)
{
    return contexte + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + Guid.NewGuid().ToString("N");
}

nomfichier1 = GenererNomFichier("DonnéesMesure");
nomfichier2 = GenererNomFichier("Image");

De cette façon, lorsque je trie par nom de fichier, les fichiers seront automatiquement regroupés par la chaîne de contexte et triés par horodatage.

Notez que la limite de caractères dans les noms de fichiers sous Windows est de 255.

2 votes

+1 Pour la suggestion d'inclure des informations utiles combinées avec un GUID. - Une remarque à prendre avec un grain de sel : inclure la date et l'heure dans un nom de fichier est un peu redondant lorsque vous pouvez simplement Clique droit > Trier par > Date.

1 votes

Le temps devient utile si vous stockez un tas de fichiers avec des contextes différents dans le même répertoire. Bien sûr, la génération du nom de fichier devrait être ajustée en fonction de vos besoins spécifiques.

0 votes

Doit être Guid.NewGuid().ToString();. Parenthèse manquante. +1 sinon

24voto

Mike Chamberlain Points 5325

Voici un algorithme qui renvoie un nom de fichier lisible unique basé sur celui fourni à l'origine. Si le fichier d'origine existe, il tente de manière incrémentielle d'ajouter un index au nom de fichier jusqu'à ce qu'il trouve un nom qui n'existe pas. Il lit les noms de fichiers existants dans un HashSet pour vérifier les collisions, donc c'est assez rapide (quelques centaines de noms de fichiers par seconde sur ma machine), c'est aussi sûr pour les threads et ne souffre pas de conditions de concurrence.

Par exemple, si vous lui passez test.txt, il tentera de créer des fichiers dans cet ordre :

test.txt
test (2).txt
test (3).txt

etc. Vous pouvez spécifier le nombre maximal de tentatives ou le laisser par défaut.

Voici un exemple complet :

class Program
{
    static FileStream CreateFileWithUniqueName(string folder, string fileName,
        int maxAttempts = 1024)
    {
        // get filename base and extension
        var fileBase = Path.GetFileNameWithoutExtension(fileName);
        var ext = Path.GetExtension(fileName);
        // build hash set of filenames for performance
        var files = new HashSet(Directory.GetFiles(folder));

        for (var index = 0; index < maxAttempts; index++)
        {
            // first try with the original filename, else try incrementally adding an index
            var name = (index == 0)
                ? fileName
                : String.Format("{0} ({1}){2}", fileBase, index, ext);

            // check if exists
            var fullPath = Path.Combine(folder, name);
            if(files.Contains(fullPath))
                continue;

            // try to create the file
            try
            {
                return new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write);
            }
            catch (DirectoryNotFoundException) { throw; }
            catch (DriveNotFoundException) { throw; }
            catch (IOException)
            {
                // Will occur if another thread created a file with this 
                // name since we created the HashSet. Ignore this and just
                // try with the next filename.
            }
        }

        throw new Exception("Impossible de créer un nom de fichier unique en " + maxAttempts + " tentatives");
    }

    static void Main(string[] args)
    {
        for (var i = 0; i < 500; i++)
        {
            using (var stream = CreateFileWithUniqueName(@"c:\temp\", "test.txt"))
            {
                Console.WriteLine("Created \"" + stream.Name + "\"");
            }
        }

        Console.ReadKey();
    }
}

0 votes

thread-safe ? pas de static readonly variable ni de lock ?

0 votes

La méthode elle-même est statique donc ne partage rien, donc je crois que plusieurs threads peuvent entrer en toute sécurité dans cette méthode simultanément. Peut-être que "thread-safe" n'est pas tout à fait le terme correct - j'essaie de faire comprendre que si un autre thread/processus crée un fichier avec un nom en conflit pendant l'exécution, cette méthode récupère et essaie le nom disponible suivant. N'hésitez pas à modifier si vous pensez que cela peut être amélioré.

0 votes

Peut-être que "ne pas souffrir d'une condition de course" est une meilleure façon de le dire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X