C #: Comment créer un nom de fichier unique en ajoutant un nombre?

Je voudrais créer une méthode qui prend soit un nom de fichier sous forme de ssortingng soit un object FileInfo et ajoute un nombre incrémenté au nom du fichier si le fichier existe. Mais je ne peux pas tout à fait comprendre comment le faire dans le bon sens.

Par exemple, si j’ai ce fichier

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

Je voudrais que la méthode me donne un nouveau FileInfo avec C: \ file 1.ext si C: \ file.ext existait, et C: \ file 2.ext si C: \ file 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. } 

 public FileInfo MakeUnique(ssortingng path) { ssortingng dir = Path.GetDirectoryName(path); ssortingng fileName = Path.GetFileNameWithoutExtension(path); ssortingng 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 course, comme indiqué dans d’autres réponses.

Beaucoup de bons conseils ici. J’ai fini par utiliser une méthode écrite par Marc pour répondre à une autre question . Le reformater un tout petit peu et append une autre méthode pour le rendre un peu plus facile à utiliser “de l’extérieur”. Voici le résultat:

 private static ssortingng numberPattern = " ({0})"; public static ssortingng NextAvailableFilename(ssortingng 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 ssortingng GetNextFilename(ssortingng pattern) { ssortingng tmp = ssortingng.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(ssortingng.Format(pattern, max))) { min = max; max *= 2; } while (max != min + 1) { int pivot = (max + min) / 2; if (File.Exists(ssortingng.Format(pattern, pivot))) min = pivot; else max = pivot; } return ssortingng.Format(pattern, max); } 

Seulement partiellement testé jusqu’à présent, mais mettra à jour si je trouve des bugs avec elle. (Le code de Marc fonctionne parfaitement!) Si vous rencontrez des problèmes, veuillez commenter ou modifier quelque chose 🙂

Pas joli, mais j’ai eu ça pendant un moment:

 private ssortingng getNextFileName(ssortingng fileName) { ssortingng 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 “Fichier (3) .txt”.

Pas le plus efficace car il n’utilise pas de recherche binary, mais devrait être correct pour un petit nombre de fichiers. Et cela ne tient pas compte de la condition de course …

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

NomFichier.AAAAMMJJ.HHMMSS

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

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

 try{ ssortingng tempFile=System.IO.Path.GetTempFileName(); ssortingng 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.

 ///  /// Create a unique filename for the given filename ///  /// A full filename, eg, C:\temp\myfile.tmp /// A filename like C:\temp\myfile633822247336197902.tmp public ssortingng GetUniqueFilename(ssortingng filename) { ssortingng basename = Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename)); ssortingng uniquefilename = ssortingng.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 improbables. Cependant, un Thread.Sleep (1) le garantira, mais je doute que cela soit nécessaire

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

L’idée est d’obtenir une liste des fichiers existants, d’parsingr les chiffres, puis d’en sélectionner un autre.

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

Note 2: Ceci n’a pas été testé.

 public static FileInfo GetNextUniqueFile(ssortingng path) { //if the given file doesn't exist, we're done if(!File.Exists(path)) return new FileInfo(path); //split the path into parts ssortingng dirName = Path.GetDirectoryName(path); ssortingng fileName = Path.GetFileNameWithoutExtension(path); ssortingng 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 ssortingngs from the existing files var NumberSsortingngs = 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 numberSsortingng in NumberSsortingngs) { int tempNum; if(Int32.TryParse(numberSsortingng, out tempnum) && tempNum > highestNumber) highestNumber = tempNum; } //make the new FileInfo object ssortingng newFileName = fileName + " " + (highestNumber + 1).ToSsortingng(); newFileName = Path.ChangeExtension(fileName, fileExt); return new FileInfo(Path.Combine(dirName, newFileName)); } 

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

 public static class FileInfoExtensions { public static FileInfo MakeUnique(this FileInfo fileInfo) { if (fileInfo == null) { throw new ArgumentNullException("fileInfo"); } ssortingng newfileName = new FileUtilities().GetNextFileName(fileInfo.FullName); return new FileInfo(newfileName); } } public class FileUtilities { public ssortingng GetNextFileName(ssortingng fullFileName) { if (fullFileName == null) { throw new ArgumentNullException("fullFileName"); } if (!File.Exists(fullFileName)) { return fullFileName; } ssortingng baseFileName = Path.GetFileNameWithoutExtension(fullFileName); ssortingng ext = Path.GetExtension(fullFileName); ssortingng filePath = Path.GetDirectoryName(fullFileName); var numbersUsed = Directory.GetFiles(filePath, baseFileName + "*" + ext) .Select(x => Path.GetFileNameWithoutExtension(x).Subssortingng(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; } } 

En voici une qui dissocie la question de dénomination numérotée de la vérification du système de fichiers:

 ///  /// Finds the next unused unique (numbered) filename. ///  /// Name of the file. /// Function that will determine if the name is already in use /// 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. private static ssortingng NextUniqueFilename(ssortingng fileName, Func 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))); 

Ceci est juste une opération de chaîne; Recherchez l’emplacement dans la chaîne de nom de fichier où vous voulez insérer le numéro et recréez une nouvelle chaîne avec le numéro inséré. Pour le rendre réutilisable, vous pouvez rechercher un numéro à cet emplacement et l’parsingr en un nombre entier pour pouvoir l’incrémenter.

Veuillez noter que cette manière de générer un nom de fichier unique est généralement non sécurisée. il y a des dangers évidents liés à la condition de course .

Il pourrait y avoir des solutions prêtes à l’emploi pour cela dans la plate-forme, je ne suis pas à la hauteur avec C #, donc je ne peux pas y aider.

Examinez les méthodes de la classe Path , en particulier Path.GetFileNameWithoutExtension () et Path.GetExtension () .

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

Modifier:

Dans le passé, j’ai utilisé la technique consistant à essayer d’écrire le fichier (avec le nom de mon choix), puis à utiliser les fonctions ci-dessus pour créer un nouveau nom si une IOException appropriée était lancée jusqu’à ce que l’opération réussisse.

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

Si le fichier existe, recherchez la position du dernier trait de soulignement. Si le contenu après le trait de soulignement est un nombre, augmentez ce nombre. sinon append le premier index. Répétez jusqu’à ce que le nom de fichier inutilisé soit trouvé.

 static public ssortingng AddIndexToFileNameIfNeeded(ssortingng sFileNameWithPath) { ssortingng 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 ssortingng sFilePath = Path.GetDirectoryName(sFileNameWithIndex); ssortingng sFileName = Path.GetFileNameWithoutExtension(sFileNameWithIndex); ssortingng sFileExtension = Path.GetExtension(sFileNameWithIndex); if (sFileName.Contains('_')) { // Need to increase the existing index by one or add first index int iIndexOfUnderscore = sFileName.LastIndexOf('_'); ssortingng sContentAfterUnderscore = sFileName.Subssortingng(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++; ssortingng sContentBeforUnderscore = sFileName.Subssortingng(0, iIndexOfUnderscore); sFileName = sContentBeforUnderscore + "_" + iCurrentIndex.ToSsortingng("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; } 
  private async Task CreateBlockBlob(CloudBlobContainer container, ssortingng blobNameToCreate) { var blockBlob = container.GetBlockBlobReference(blobNameToCreate); var i = 1; while (await blockBlob.ExistsAsync()) { var newBlobNameToCreate = CreateRandomFileName(blobNameToCreate,i.ToSsortingng()); blockBlob = container.GetBlockBlobReference(newBlobNameToCreate); i++; } return blockBlob; } private ssortingng CreateRandomFileName(ssortingng fileNameWithExtension, ssortingng prefix=null) { int fileExtPos = fileNameWithExtension.LastIndexOf(".", SsortingngComparison.Ordinal); if (fileExtPos >= 0) { var ext = fileNameWithExtension.Subssortingng(fileExtPos, fileNameWithExtension.Length - fileExtPos); var fileName = fileNameWithExtension.Subssortingng(0, fileExtPos); return Ssortingng.Format("{0}_{1}{2}", fileName, Ssortingng.IsNullOrWhiteSpace(prefix) ? new Random().Next(int.MinValue, int.MaxValue).ToSsortingng():prefix,ext); } //This means there is no Extension for the file and its fine attaching random number at the end. return Ssortingng.Format("{0}_{1}", fileNameWithExtension, new Random().Next(int.MinValue, int.MaxValue)); } 

J’utilise ce code pour créer un nom de fichier consécutif _1, _2, _3 etc. à chaque fois qu’un fichier existe dans le stockage d’objects blob.

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

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

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 si difficile que les autres, et c'est une réponse simple à la demande de l'utilisateur.