C#: Как бы вы сделали уникальное имя файла, добавив число?

Я хотел бы создать метод, который принимает имя файла как string или FileInfo и добавляет к имени файла увеличенное число, если файл существует. Но никак не могу понять, как это сделать по-хорошему.

Например, если у меня есть эта FileInfo

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

Я хотел бы, чтобы метод предоставил мне новую FileInfo с C:\file 1.ext, если C:\file.ext существовал, и C:\file 2.ext, если существует C:\file 1.ext и т.д. Что-то вроде этого:

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

person Svish    schedule 03.07.2009    source источник
comment
Это было задано некоторое время назад ... постараюсь откопать его   -  person Sam Saffron    schedule 03.07.2009
comment
ой. пробовал искать, но ничего не нашел.   -  person Svish    schedule 03.07.2009
comment
@svish, посмотри мой ответ: вы не сможете найти этот вопрос, если не знаете, что он существует, у него было очень загадочное описание   -  person Sam Saffron    schedule 03.07.2009
comment
хе-хе, как раз вопрос, который я собирался задать.   -  person Maxim Zaslavsky    schedule 03.07.2009


Ответы (19)


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

Очевидно, что это уязвимо для условий гонки, как указано в других ответах.

person mmx    schedule 03.07.2009
comment
Это действительно неэффективно для большого количества файлов, лучше использовать бинарный поиск. - person Sam Saffron; 03.07.2009
comment
Один из способов (помочь) устранить состояние гонки — вместо этого вернуть в файл открытый поток, а не только информацию о файле. - person Matthew Scharley; 03.07.2009
comment
@Sam: Напиши свое. Я собираюсь поспать ;) - person mmx; 03.07.2009
comment
@ Мэтью: Как бы вы это сделали? - person Svish; 03.07.2009
comment
нет необходимости, Марк уже сделал: p stackoverflow.com/questions/909521/ - person Sam Saffron; 03.07.2009
comment
Условие гонки заключается в том, что кто-то другой может открыть файл раньше вас... открытие как часть того, что делает функция, устраняет это (если это не удается, вы продолжаете искать новый файл) - person Matthew Scharley; 03.07.2009
comment
@Matthew Как бы вы написали метод, который делал бы это таким образом, чтобы предотвратить условия гонки? - person Svish; 03.07.2009
comment
-1 - Не знаю, почему в этом ответе так много взлетов, он не будет делать то, о чем задавался вопрос. Вместо увеличения и замены числа в имени файла он просто добавит его (код ничего не делает для удаления исходного числа и замены его увеличенным). - person bytedev; 29.01.2013
comment
Согласитесь с @nashwan, это приводит к нежелательным результатам. - person Jeb; 30.09.2014
comment
Размещение открывающей скобки в операторе if меня действительно смутило. Мне понадобилось 20 секунд, чтобы заметить это, ха-ха. - person Owen James; 04.07.2016

Здесь много хороших советов. В итоге я использовал метод, написанный Marc в ответ на другой вопрос. Немного переформатировал его и добавил еще один метод, чтобы его было немного проще использовать «извне». Вот результат:

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

Пока только частично протестировал его, но обновлю, если найду какие-либо ошибки. (код Марка работает отлично!) Если вы обнаружите какие-либо проблемы с ним, прокомментируйте, отредактируйте или что-то в этом роде :)

person Community    schedule 03.07.2009
comment
обратите внимание, из моего бенчмаркинга видно, что пока у вас есть более 170 файлов в вашем каталоге, это быстрее, чем просто получить все файлы в каталоге и выполнить всю работу в памяти. - person Sam Saffron; 03.07.2009
comment
@ Сэм: Спасибо. Деф приятно знать. Мои папки, вероятно, даже не будут близки к этому, но приятно знать, что это будет эффективно, если это произойдет. Хороший служебный метод, который можно использовать :) - person Svish; 03.07.2009
comment
Что делать, если NextAvailableFilename(MyFile (1).txt)? Является ли результат MyFile (1) (1).txt? Разве это не должен быть MyFile (2).txt? - person J Pollack; 11.04.2013
comment
Нет, не должно, потому что эта функция ожидает, что путь будет таким, каким вы хотите его видеть. Не то, как файл случается. Конечно, вы можете настроить функцию, если хотите, чтобы она вел себя по-другому, но вам придется сделать это самостоятельно ;) - person Svish; 12.04.2013
comment
Извините, что поднимаю старый вопрос, но может ли кто-нибудь объяснить мне, как работает этот подход? Я не понимаю, как и почему GetNextFilename (шаблон строки) использует свод, максимум и минимум. - person misleadingTitle; 24.09.2013
comment
@misleadingTitle Прошло некоторое время, но я думаю, что он выполняет своего рода двоичный поиск доступных имен файлов. Это делается для того, чтобы не проверять наличие 1000 файлов, если, например, следующим доступным будет 1001. Я не помню, как именно работает функция pivot,max,min, но если вам интересно, просто вставьте точку останова и пройдитесь по ней в отладчике. Должно быть довольно легко следовать :) - person Svish; 25.09.2013
comment
На самом деле это просто добавляет ({0}) к существующему имени файла, поэтому, если имя файла /temp/myFilename (1).txt, вы получите /temp/myFilename (1)({0}).txt в качестве шаблона, переданного в GetNextFilename() — который создаст следующий путь: /temp/myFilename (1)(1).txt — не совсем то, что нужно. - person mdebeus; 27.07.2018
comment
@mdebeus Прочтите мой предыдущий комментарий. Если вы проходите в /tmp/myFile (1).txt, то да, это то, что вы получите. Вы должны пройти через /tmp/myFile.txt. Вы передаете желаемое имя, а не существующее имя. - person Svish; 27.07.2018
comment
Это замечательно. Это дало мне ценную информацию, позволяющую устранить постоянный недостаток дизайна, над которым я работал. Очень полезный. - person tobbyioa; 26.06.2020

Не красиво, но у меня было это некоторое время:

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

Предполагая, что файлы уже существуют:

  • Файл.txt
  • Файл(1).txt
  • Файл(2).txt

вызов getNextFileName("File.txt") вернет "File(3).txt".

Не самый эффективный, потому что он не использует бинарный поиск, но должен подойти для небольшого количества файлов. И это не принимает во внимание состояние гонки...

person Pierre-Olivier Goulet    schedule 03.07.2014
comment
Мне нравится ваше решение, потому что оно простое и хорошо работает для небольшого количества файлов! - person Teo; 21.10.2019

Если проверка существования файла слишком сложна, вы всегда можете просто добавить дату и время к имени файла, чтобы сделать его уникальным:

ИмяФайла.ГГГГММДД.ЧЧММСС

Может быть, даже добавить миллисекунды, если это необходимо.

person mga911    schedule 03.07.2009
comment
Я успешно использовал эту технику. Если вы создаете файлы слишком быстро, вы рискуете столкнуться с конфликтом имен, но если вы знаете, что не создаете несколько файлов в миллисекундах друг от друга, это прекрасно работает. - person Jay; 03.07.2009
comment
Если порядок не важен, вы также можете использовать генерацию случайных чисел со счетчиком и pid. - person HeretoLearn; 04.07.2009
comment
Вы можете просто изменить его на ГГГГММДД.ЧЧММСС, чтобы решить проблему с заказом... - person Romias; 28.06.2016
comment
И код C# для создания суффикса выглядит следующим образом: DateTime.UtcNow.ToString("yyyyMMddHHmmss") (с учетом регистра). - person Borislav Ivanov; 22.05.2017


/// <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;
}

Поскольку DateTime.Ticks имеет разрешение 100 наносекунд , столкновения крайне маловероятны. Тем не менее, Thread.Sleep(1) обеспечит это, но я сомневаюсь, что это необходимо

person Michael Stum    schedule 03.07.2009

Вставьте новый GUID в имя файла.

person Daniel Earwicker    schedule 03.07.2009
comment
Я поражен, что никто другой не сказал этого, все остальное было бы пустой тратой усилий (если вы действительно не хотите, чтобы ваши временные имена файлов выглядели красиво... но почему?!) - person Daniel Earwicker; 03.07.2009
comment
Потому что файлы не временные? У меня уже есть желаемое имя. Мне просто нужно добавить к нему номер, если файл с таким именем уже существует, чтобы я не перезаписывал уже существующий. - person Svish; 03.07.2009
comment
Хорошим примером того, где это необходимо, является создание Visual Studio нового класса. Если Class1.cs уже существует, он создает Class2.cs и т. д. Для VS было бы нелепо создавать новый класс с GUID в качестве имени. - person Xcalibur; 09.03.2011

Идея состоит в том, чтобы получить список существующих файлов, проанализировать числа, а затем создать следующий по величине.

Примечание. Это уязвимо для условий гонки, поэтому, если у вас более одного потока, создающего эти файлы, будьте осторожны.

Примечание 2: Это не проверено.

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));
}
person lc.    schedule 03.07.2009
comment
попытался внести ясность в вопрос. это был следующий несуществующий файл, который я хотел, но написал его немного криво :p - person Svish; 03.07.2009
comment
Это то, что я понял, и мой ответ делает это. :) - person lc.; 03.07.2009

Вместо того, чтобы несколько раз ковыряться в диске, чтобы узнать, есть ли на нем тот или иной вариант имени нужного файла, вы можете запросить список уже существующих файлов и найти первый пробел по вашему алгоритму.

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;
    }
}    
person Handcraftsman    schedule 04.07.2009

Вот тот, который отделяет вопрос нумерованного именования от проверки файловой системы:

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

И вот как вы могли бы назвать это, если вы используете файловую систему

var safeName = NextUniqueFilename(filename, f => File.Exists(Path.Combine(folder, f)));
person Tim Abell    schedule 21.03.2012
comment
Неужели там точка с пределом? У файловой системы есть такое ограничение? - person Svish; 21.03.2012
comment
Ограничение зависит от вас, лично я не хотел, чтобы что-то попало в бесконечный цикл, и для меня 10000 файлов вряд ли когда-либо произойдет ;-) Да, и, кстати, вышесказанное не касается проблем с производительностью, которые есть у других. идентифицировали, возможно, кто-то мог бы сделать комбинированный вариант. - person Tim Abell; 21.03.2012
comment
Не понимаю, как это могло бы попасть в бесконечный цикл, если бы у вас не было бесконечного количества файлов: p - person Svish; 21.03.2012
comment
Это может произойти из-за ошибки кодирования (например, передача f => true или что-то более запутанное в качестве второго параметра). Достижение максимума будет проще устранить неполадки, чем наблюдать, как ваш процессор загружается до 100% и ждать целочисленного переполнения. - person Tim Abell; 21.03.2012
comment
Конечно, но я предпочитаю не устанавливать такие произвольные ограничения :) (Если бы мне нужно было создать исключение, я бы использовал более конкретное, чем Exception... ;) - person Svish; 21.03.2012
comment
Это, безусловно, разумные моменты. С введением max я уравновешиваю простоту устранения неполадок в неконтролируемом процессе и произвольные ограничения. Приветствуются более элегантные подходы ;-). Исключения Wrt: я полагаю, что мог бы создать собственный класс исключений, но это кажется излишним, если у меня нет намерения его поймать (чего лично я не делаю на этот раз). Я не мог придумать стандартный класс исключений, который отвечал бы всем требованиям. Предложения приветствуются. - person Tim Abell; 21.03.2012

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

Я использую этот код для создания последовательного имени файла _1,_2,_3 и т. д. каждый раз, когда файл существует в хранилище больших двоичных объектов.

person Avinash Gadiraju    schedule 02.10.2015

Это ответ на вопрос в этой ссылке, но они пометили его как дубликат, поэтому я публикую свой ответ здесь.

Я создал этот класс проверки концепции (может содержать ошибки). Дополнительные пояснения в комментариях к коду.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace ConsoleApp
{
    class Program
    {
        static void Main( string[] args )
        {
            var testFilePaths = new List<string>
            {
                @"c:\test\file.txt",
                @"c:\test\file(1).txt",
                @"c:\test\file(2).txt",
                @"c:\TEST2\file(3).txt",
                @"c:\test\file(5).txt",
                @"c:\test\file(5)abc.txt",
                @"c:\test\file(5).avi"
            };

            // inspect in debbuger for correct values
            var withSuffix      = new DecomposedFilePath( "c:\\files\\file(13).txt");
            var withoutSuffix   = new DecomposedFilePath( "c:\\files\\file(abc).txt");
            var withExtraNumber = new DecomposedFilePath( "c:\\files\\file(34)xyz(35).txt"); // "(34)" in the middle should be ignored

            DecomposedFilePath changedSuffix = withExtraNumber.ReplaceSuffix( 1999 ); // "file(34)xyz(35).txt" -> "file(34)xyz(1999).txt"
            DecomposedFilePath removedSuffix = changedSuffix.ReplaceSuffix( null ); // "file(34)xyz(1999).txt" -> "file(34)xyz.txt"

            var testPath = new DecomposedFilePath( "c:\\test\\file.txt");
            DecomposedFilePath nextPath1 = testPath.GetFirstFreeFilePath( testFilePaths );

            // update our list
            testFilePaths.Add( nextPath1.FullFilePath );
            DecomposedFilePath nextPath2 = testPath.GetFirstFreeFilePath( testFilePaths );
        
            testFilePaths.Add( nextPath2.FullFilePath );
            DecomposedFilePath nextPath3 = testPath.GetFirstFreeFilePath( testFilePaths );
        }
    }

    public sealed class DecomposedFilePath
    {
        public DecomposedFilePath( string filePath )
        {
            FullFilePath = Path.GetFullPath( filePath );
        }

        // "c:\myfiles\file(4).txt"
        public string FullFilePath { get; }

        // "file" or "file(1)"
        public string FileNameWithoutExt => Path.GetFileNameWithoutExtension( FullFilePath );

        // "file(13)" -> "file"
        public string FileNameWithoutExtAndSuffix => FileNameWithoutExt.Substring( 0, FileNameWithoutExt.Length - Suffix.Length ); // removes suffix

        // ".txt"
        public string Extenstion => Path.GetExtension( FullFilePath );

        // "c:\myfiles"
        public string DirectoryPath => Path.GetDirectoryName( FullFilePath );

        // "file(23)" -> "23", file -> stirng.Empty
        public string Suffix
        {
            get
            {
                // we want to extract suffix from file name, e.g. "(34)" from "file(34)"
                // I am not good at regex, but I hope it will work correctly

                var regex = new Regex( @"\([0-9]+\)$" );
                Match match = regex.Match( FileNameWithoutExt );

                if (!match.Success) return string.Empty; // suffix not found

                return match.Value; // return "(number)"
            }
        }

        // tranlates suffix "(33)" to 33. If suffix is does not exist (string.empty), returns null (int?)
        public int? SuffixAsInt
        {
            get
            {
                if (Suffix == string.Empty) return null;

                string numberOnly = Suffix.Substring( 1, Suffix.Length - 2 ); // remove '(' from beginning and ')' from end

                return int.Parse( numberOnly );
            }
        }

        // e.g. input is suffix: 56 then it changes file name from "file(34)" to "file(56)"
        public DecomposedFilePath ReplaceSuffix( int? suffix ) // null - removes suffix
        {
            string strSuffix = suffix is null ? string.Empty : $"({suffix})"; // add ( and )

            string path = Path.Combine( DirectoryPath, FileNameWithoutExtAndSuffix + strSuffix + Extenstion ); // build full path

            return new DecomposedFilePath( path );
        }

        public DecomposedFilePath GetFirstFreeFilePath( IEnumerable<string> filesInDir )
        {
            var decomposed = filesInDir
                // convert all paths to our class
                .Select( x => new DecomposedFilePath( x ) )
                // pick files only with the same extensionm as our base file, ignore case
                .Where( x => string.Equals( Extenstion, x.Extenstion, StringComparison.OrdinalIgnoreCase) )
                // pick files only with the same name (ignoring suffix)
                .Where( x => string.Equals( FileNameWithoutExtAndSuffix, x.FileNameWithoutExtAndSuffix, StringComparison.OrdinalIgnoreCase) )
                // with the same directory
                .Where( x => string.Equals( DirectoryPath, x.DirectoryPath, StringComparison.OrdinalIgnoreCase) )
                .ToList(); // create copy for easier debugging

            if (decomposed.Count == 0) return this; // no name collision

            int? firstFreeSuffix = Enumerable.Range( 1, int.MaxValue) // start numbering duplicates from 1
                                  .Select( x => (int?) x) // change to int? because SuffixAsInt is of that type
                                  .Except( decomposed.Select( x => x.SuffixAsInt) ) // remove existing suffixes
                                  .First(); // get first free suffix

            return ReplaceSuffix( firstFreeSuffix );
        }

        public override string ToString() => FullFilePath;
    }
}
person apocalypse    schedule 17.10.2020

Я должен добавить свои 2 цента. Вот как я это сделал, и это работает для моего использования.

    private static string IterateFileName(string fileName)
    {
        if (!File.Exists(fileName)) return fileName;

        FileInfo fi = new FileInfo(fileName);
        string ext = fi.Extension;
        string name = fi.FullName.Substring(0, fi.FullName.Length - ext.Length);

        int i = 2;
        while (File.Exists($"{name}_{i}{ext}"))
        {
            i++;
        }


        return $"{name}_{i}{ext}";
    }
person tolsen64    schedule 23.03.2021

Это просто строковая операция; найдите место в строке имени файла, куда вы хотите вставить число, и заново создайте новую строку со вставленным числом. Чтобы сделать его пригодным для повторного использования, вы можете найти число в этом месте и преобразовать его в целое число, чтобы увеличить его.

Обратите внимание, что в целом этот способ создания уникального имени файла небезопасен; существуют очевидные условия гонки.

В платформе могут быть готовые решения для этого, я не в курсе C#, поэтому помочь не могу.

person unwind    schedule 03.07.2009

Взгляните на методы в классе Path. , в частности Path.GetFileNameWithoutExtension(), и Path.GetExtension().

Вы даже можете найти Path.GetRandomFileName() полезно!

Изменить:

В прошлом я использовал метод попытки записи файла (с желаемым именем), а затем использования вышеуказанных функций для создания нового имени, если выдается соответствующий IOException, повторяя это до тех пор, пока не добьется успеха.

person Steve Guidi    schedule 03.07.2009

Этот метод добавит индекс к существующему файлу, если это необходимо:

Если файл существует, найдите позицию последнего подчеркивания. Если содержимое после подчеркивания является числом, увеличьте это число. в противном случае добавьте первый индекс. повторять до тех пор, пока не будет найдено неиспользуемое имя файла.

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;
}
person Gil Epshtain    schedule 04.01.2015

Надеюсь, что эта самоповторяющаяся функция может помочь. Он отлично работает для меня.

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; 
    }
person Bikuz    schedule 02.03.2018

Я сделал это так:

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
            }
        }

Я думаю, что эта версия не так сложна, как другие, и это прямой ответ на то, что хотел пользователь.

person Hélder Pinto    schedule 28.02.2015

Если вам нужно просто уникальное имя файла, как насчет этого?

Path.GetRandomFileName()
person Oleg    schedule 06.11.2018