web-dev-qa-db-fra.com

C #: Comment feriez-vous un nom de fichier unique en ajoutant un numéro?

Je voudrais créer une méthode qui prend un nom de fichier en tant que string ou FileInfo et ajoute un numéro incrémenté au nom de fichier si le fichier existe. Mais je ne peux pas vraiment comprendre comment faire cela dans le bon sens.

Par exemple, si j'ai ce FileInfo

var file = new FileInfo(@"C:\file.ext");

Je voudrais que la méthode me donne un nouveau FileInfo avec C:\fichier 1.ext si C:\fichier.ext Existait et C:\fichier 2.ext if C:\fichier 1.ext existait et ainsi de suite. Quelque chose comme ça:

public FileInfo MakeUnique(FileInfo fileInfo)
{
    if(fileInfo == null)
        throw new ArgumentNullException("fileInfo");
    if(!fileInfo.Exists)
        return fileInfo;

    // Somehow construct new filename from the one we have, test it, 
    // then do it again if necessary.
}
40
Svish

Beaucoup de bons conseils ici. J'ai fini par utiliser une méthode écrite par Marc dans une réponse à une question différente . Reformaté un tout petit peu et ajouté une autre méthode pour le rendre un peu plus facile à utiliser "de l'extérieur". Voici le résultat:

private static string numberPattern = " ({0})";

public static string NextAvailableFilename(string path)
{
    // Short-cut if already available
    if (!File.Exists(path))
        return path;

    // If path has extension then insert the number pattern just before the extension and return next filename
    if (Path.HasExtension(path))
        return GetNextFilename(path.Insert(path.LastIndexOf(Path.GetExtension(path)), numberPattern));

    // Otherwise just append the pattern to the path and return next filename
    return GetNextFilename(path + numberPattern);
}

private static string GetNextFilename(string pattern)
{
    string tmp = string.Format(pattern, 1);
    if (tmp == pattern)
        throw new ArgumentException("The pattern must include an index place-holder", "pattern");

    if (!File.Exists(tmp))
        return tmp; // short-circuit if no matches

    int min = 1, max = 2; // min is inclusive, max is exclusive/untested

    while (File.Exists(string.Format(pattern, max)))
    {
        min = max;
        max *= 2;
    }

    while (max != min + 1)
    {
        int pivot = (max + min) / 2;
        if (File.Exists(string.Format(pattern, pivot)))
            min = pivot;
        else
            max = pivot;
    }

    return string.Format(pattern, max);
}

Ce n’est que partiellement testé jusqu’à présent, mais je le mettrai à jour si je trouve des bogues. ( Marc s le code fonctionne bien!) Si vous rencontrez un problème, merci de le commenter, de le modifier ou quelque chose du genre :)

25
Svish
public FileInfo MakeUnique(string path)
{            
    string dir = Path.GetDirectoryName(path);
    string fileName = Path.GetFileNameWithoutExtension(path);
    string fileExt = Path.GetExtension(path);

    for (int i = 1; ;++i) {
        if (!File.Exists(path))
            return new FileInfo(path);

        path = Path.Combine(dir, fileName + " " + i + fileExt);
    }
}

De toute évidence, cela est vulnérable aux conditions de concurrence, comme indiqué dans d'autres réponses.

47
Mehrdad Afshari

Pas joli, mais ça fait un moment que je l'ai:

private string getNextFileName(string fileName)
{
    string extension = Path.GetExtension(fileName);

    int i = 0;
    while (File.Exists(fileName))
    {
        if (i == 0)
            fileName = fileName.Replace(extension, "(" + ++i + ")" + extension);
        else
            fileName = fileName.Replace("(" + i + ")" + extension, "(" + ++i + ")" + extension);
    }

    return fileName;
}

En supposant que les fichiers existent déjà:

  • Fichier.txt
  • Fichier (1) .txt
  • Fichier (2) .txt

l'appel getNextFileName ("File.txt") renverra "File (3) .txt".

Ce n’est pas le plus efficace car il n’utilise pas la recherche binaire, mais devrait convenir pour un petit nombre de fichiers. Et cela ne prend pas en compte les conditions de concurrence….

15

Si vérifier si le fichier existe est trop difficile, vous pouvez toujours ajouter une date et une heure au nom du fichier pour le rendre unique:

NomFichier.AAAAMMJJ.HHMMSS

Peut-être même ajouter des millisecondes si nécessaire.

11
mga911

Si le format ne vous dérange pas, vous pouvez appeler:

try{
    string tempFile=System.IO.Path.GetTempFileName();
    string file=System.IO.Path.GetFileName(tempFile);
    //use file
    System.IO.File.Delete(tempFile);
}catch(IOException ioe){
  //handle 
}catch(FileIOPermission fp){
  //handle
}

PS: - S'il vous plaît lire plus à ce sujet à msdn avant d'utiliser.

5
TheVillageIdiot
/// <summary>
/// Create a unique filename for the given filename
/// </summary>
/// <param name="filename">A full filename, e.g., C:\temp\myfile.tmp</param>
/// <returns>A filename like C:\temp\myfile633822247336197902.tmp</returns>
public string GetUniqueFilename(string filename)
{
    string basename = Path.Combine(Path.GetDirectoryName(filename),
                                   Path.GetFileNameWithoutExtension(filename));
    string uniquefilename = string.Format("{0}{1}{2}",
                                            basename,
                                            DateTime.Now.Ticks,
                                            Path.GetExtension(filename));
    // Thread.Sleep(1); // To really prevent collisions, but usually not needed
    return uniquefilename;
}

Comme DateTime.Ticks a une résolution de 100 nanosecondes , les collisions sont extrêmement peu probables. Cependant, un Thread.Sleep (1) le fera, mais je doute que cela soit nécessaire

4
Michael Stum

Insérez un nouveau GUID dans le nom du fichier.

3
Daniel Earwicker

Au lieu de chercher plusieurs fois le disque pour savoir s'il contient une variante particulière du nom de fichier souhaité, vous pouvez demander la liste des fichiers existants et trouver le premier espace en fonction de votre algorithme.

public static class FileInfoExtensions
{
    public static FileInfo MakeUnique(this FileInfo fileInfo)
    {
        if (fileInfo == null)
        {
            throw new ArgumentNullException("fileInfo");
        }

        string newfileName = new FileUtilities().GetNextFileName(fileInfo.FullName);
        return new FileInfo(newfileName);
    }
}

public class FileUtilities
{
    public string GetNextFileName(string fullFileName)
    {
        if (fullFileName == null)
        {
            throw new ArgumentNullException("fullFileName");
        }

        if (!File.Exists(fullFileName))
        {
            return fullFileName;
        }
        string baseFileName = Path.GetFileNameWithoutExtension(fullFileName);
        string ext = Path.GetExtension(fullFileName);

        string filePath = Path.GetDirectoryName(fullFileName);
        var numbersUsed = Directory.GetFiles(filePath, baseFileName + "*" + ext)
            .Select(x => Path.GetFileNameWithoutExtension(x).Substring(baseFileName.Length))
            .Select(x =>
                    {
                        int result;
                        return Int32.TryParse(x, out result) ? result : 0;
                    })
            .Distinct()
            .OrderBy(x => x)
            .ToList();

        var firstGap = numbersUsed
            .Select((x, i) => new { Index = i, Item = x })
            .FirstOrDefault(x => x.Index != x.Item);
        int numberToUse = firstGap != null ? firstGap.Item : numbersUsed.Count;
        return Path.Combine(filePath, baseFileName) + numberToUse + ext;
    }
}    
1
Handcraftsman

L’idée est d’obtenir une liste des fichiers existants, d’analyser les nombres, puis d’augmenter le prochain.

Note: Ceci est vulnérable aux conditions de concurrence, donc si vous avez plusieurs threads créant ces fichiers, soyez prudent .

Note 2: Ceci n'est pas testé.

public static FileInfo GetNextUniqueFile(string path)
{
    //if the given file doesn't exist, we're done
    if(!File.Exists(path))
        return new FileInfo(path);

    //split the path into parts
    string dirName = Path.GetDirectoryName(path);
    string fileName = Path.GetFileNameWithoutExtension(path);
    string fileExt = Path.GetExtension(path);

    //get the directory
    DirectoryInfo dir = new DirectoryInfo(dir);

    //get the list of existing files for this name and extension
    var existingFiles = dir.GetFiles(Path.ChangeExtension(fileName + " *", fileExt);

    //get the number strings from the existing files
    var NumberStrings = from file in existingFiles
                        select Path.GetFileNameWithoutExtension(file.Name)
                            .Remove(0, fileName.Length /*we remove the space too*/);

    //find the highest existing number
    int highestNumber = 0;

    foreach(var numberString in NumberStrings)
    {
        int tempNum;
        if(Int32.TryParse(numberString, out tempnum) && tempNum > highestNumber)
            highestNumber = tempNum;
    }

    //make the new FileInfo object
    string newFileName = fileName + " " + (highestNumber + 1).ToString();
    newFileName = Path.ChangeExtension(fileName, fileExt);

    return new FileInfo(Path.Combine(dirName, newFileName));
}
1
lc.

En voici une qui sépare la question de nommage numérotée de la vérification du système de fichiers

/// <summary>
/// Finds the next unused unique (numbered) filename.
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <param name="inUse">Function that will determine if the name is already in use</param>
/// <returns>The original filename if it wasn't already used, or the filename with " (n)"
/// added to the name if the original filename is already in use.</returns>
private static string NextUniqueFilename(string fileName, Func<string, bool> inUse)
{
    if (!inUse(fileName))
    {
        // this filename has not been seen before, return it unmodified
        return fileName;
    }
    // this filename is already in use, add " (n)" to the end
    var name = Path.GetFileNameWithoutExtension(fileName);
    var extension = Path.GetExtension(fileName);
    if (name == null)
    {
        throw new Exception("File name without extension returned null.");
    }
    const int max = 9999;
    for (var i = 1; i < max; i++)
    {
        var nextUniqueFilename = string.Format("{0} ({1}){2}", name, i, extension);
        if (!inUse(nextUniqueFilename))
        {
            return nextUniqueFilename;
        }
    }
    throw new Exception(string.Format("Too many files by this name. Limit: {0}", max));
}

Et voici comment vous pourriez l'appeler si vous utilisez le système de fichiers

var safeName = NextUniqueFilename(filename, f => File.Exists(Path.Combine(folder, f)));
1
Tim Abell

Cette méthode ajoutera un index au fichier existant si nécessaire:

Si le fichier existe, trouvez la position du dernier trait de soulignement. Si le contenu après le trait de soulignement est un nombre, augmentez ce nombre. sinon ajoutez le premier index. répéter jusqu'à ce que le nom de fichier inutilisé soit trouvé.

static public string AddIndexToFileNameIfNeeded(string sFileNameWithPath)
{
    string sFileNameWithIndex = sFileNameWithPath;

    while (File.Exists(sFileNameWithIndex)) // run in while scoop so if after adding an index the the file name the new file name exist, run again until find a unused file name
    { // File exist, need to add index

        string sFilePath = Path.GetDirectoryName(sFileNameWithIndex);
        string sFileName = Path.GetFileNameWithoutExtension(sFileNameWithIndex);
        string sFileExtension = Path.GetExtension(sFileNameWithIndex);

        if (sFileName.Contains('_'))
        { // Need to increase the existing index by one or add first index

            int iIndexOfUnderscore = sFileName.LastIndexOf('_');
            string sContentAfterUnderscore = sFileName.Substring(iIndexOfUnderscore + 1);

            // check if content after last underscore is a number, if so increase index by one, if not add the number _01
            int iCurrentIndex;
            bool bIsContentAfterLastUnderscoreIsNumber = int.TryParse(sContentAfterUnderscore, out iCurrentIndex);
            if (bIsContentAfterLastUnderscoreIsNumber)
            {
                iCurrentIndex++;
                string sContentBeforUnderscore = sFileName.Substring(0, iIndexOfUnderscore);

                sFileName = sContentBeforUnderscore + "_" + iCurrentIndex.ToString("000");
                sFileNameWithIndex = sFilePath + "\\" + sFileName + sFileExtension;
            }
            else
            {
                sFileNameWithIndex = sFilePath + "\\" + sFileName + "_001" + sFileExtension;
            }
        }
        else
        { // No underscore in file name. Simple add first index
            sFileNameWithIndex = sFilePath + "\\" + sFileName + "_001" + sFileExtension;
        }
    }

    return sFileNameWithIndex;
}
0
Gil Epshtain
    private async Task<CloudBlockBlob> CreateBlockBlob(CloudBlobContainer container,  string blobNameToCreate)
    {
        var blockBlob = container.GetBlockBlobReference(blobNameToCreate);

        var i = 1;
        while (await blockBlob.ExistsAsync())
        {
            var newBlobNameToCreate = CreateRandomFileName(blobNameToCreate,i.ToString());
            blockBlob = container.GetBlockBlobReference(newBlobNameToCreate);
            i++;
        }

        return blockBlob;
    }



    private string CreateRandomFileName(string fileNameWithExtension, string prefix=null)
    {

        int fileExtPos = fileNameWithExtension.LastIndexOf(".", StringComparison.Ordinal);

        if (fileExtPos >= 0)
        {
            var ext = fileNameWithExtension.Substring(fileExtPos, fileNameWithExtension.Length - fileExtPos);
            var fileName = fileNameWithExtension.Substring(0, fileExtPos);

            return String.Format("{0}_{1}{2}", fileName, String.IsNullOrWhiteSpace(prefix) ? new Random().Next(int.MinValue, int.MaxValue).ToString():prefix,ext);
        }

        //This means there is no Extension for the file and its fine attaching random number at the end.
        return String.Format("{0}_{1}", fileNameWithExtension, new Random().Next(int.MinValue, int.MaxValue));
    }

J'utilise ce code pour créer un nom de fichier _1, _2, _3 etc.

0
Avinash Gadiraju

J'espère que cette fonction auto-itérative peut aider. Ça fonctionne bien pour moi.

public string getUniqueFileName(int i, string filepath, string filename)
    {
        string path = Path.Combine(filepath, filename);
        if (System.IO.File.Exists(path))
        {
            string name = Path.GetFileNameWithoutExtension(filename);
            string ext = Path.GetExtension(filename);
            i++;
            filename = getUniqueFileName(i, filepath, name + "_" + i + ext);
        }
        return filename; 
    }
0
Bikuz

Je l'ai fait comme ça:

for (int i = 0; i <= 500; i++) //I suppose the number of files will not pass 500
        {       //Checks if C:\log\log+TheNumberOfTheFile+.txt exists...
            if (System.IO.File.Exists(@"C:\log\log"+conta_logs+".txt"))
            {
                conta_logs++;//If exists, then increment the counter
            }
            else
            {              //If not, then the file is created
                var file = System.IO.File.Create(@"C:\log\log" + conta_logs + ".txt");
                break; //When the file is created we LEAVE the *for* loop
            }
        }

Je pense que cette version n’est pas aussi dure que les autres et c’est une réponse simple à ce que l’utilisateur voulait.

0
Hélder Pinto

Si vous avez juste besoin d'un nom de fichier unique, pourquoi pas?

Path.GetRandomFileName()
0
Oleg

Ceci est juste une opération de chaîne; recherchez l'emplacement dans la chaîne du nom de fichier où vous souhaitez insérer le numéro et reconstruisez une nouvelle chaîne avec le numéro inséré. Pour le rendre réutilisable, vous souhaiterez peut-être rechercher un nombre situé à cet emplacement et l'analyser en un entier afin de pouvoir l'incrémenter.

Veuillez noter qu'en général, cette manière de générer un nom de fichier unique n'est pas sécurisée. il y a des dangers évidents conditions de concurrence .

Il y a peut-être des solutions toutes faites à cela dans la plate-forme, je ne suis pas au courant de C #, donc je ne peux rien y faire.

0
unwind

Jetez un coup d'œil aux méthodes de la classe Path class, en particulier Path.GetFileNameWithoutExtension () , et Path.GetExtension () .

Vous pouvez même trouver Path.GetRandomFileName () utile!

Modifier:

Auparavant, j’utilisais la technique consistant à essayer d’écrire le fichier (avec le nom souhaité), puis à utiliser les fonctions ci-dessus pour créer un nouveau nom si une variable IOException appropriée était renvoyée et se répétait jusqu’à succès.

0
Steve Guidi