2011-01-11 8 views
107

Я реализовал алгоритм, который будет генерировать уникальные имена для файлов, которые будут сохраняться на жестком диске. Я добавляю DateTime: Часы, минуты, секунды и миллисекунды, но все же он генерирует дублирующее имя файлов, потому что я загружаю несколько файлов одновременно.Как создать уникальные имена файлов в C#

Какое оптимальное решение для создания уникальных имен для файлов, которые будут храниться на жестком диске, поэтому нет двух файлов одинаковых?

ответ

192

Если читаемость не имеет значения, используйте GUIDs.

т.д .:

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

или shorter:

var myUniqueFileName = [email protected]"{Guid.NewGuid()}.txt"; 

В своих программах я иногда стараюсь, например, 10 раз для создания читаемого имени («Image1.png» .. «Image10.png»), и если это не удается (поскольку файл уже существует), я возвращаюсь к идентификаторам GUID.

Update:

В последнее время я также использовать DateTime.Now.Ticks вместо GUIDs:

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

или

var myUniqueFileName = [email protected]"{DateTime.Now.Ticks}.txt"; 

Выгода для меня является то, что это создает более короткий и «более приятное» имя файла по сравнению с идентификаторами GUID.

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

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

+4

Мне нравится использовать Ticks как GUID действительно некрасиво. Вы также можете получить хэш Ticks, который уменьшает длину символа имени файла. 'DateTime.Now.Ticks.GetHashCode(). ToString (" x "). ToUpper()' – WillMcKill

60
System.IO.Path.GetRandomFileName() 

Path.GetRandomFileName() on MSDN.

+0

Вот ссылка на документ MSDN: http://msdn.microsoft.com/en-us/library/system. io.path.getrandomfilename.aspx – epotter

+11

@RyBolt: Поскольку вам не нужно сажать себя, вы почти ничего не должны учитывать при использовании этого метода. И я бы ожидал, что подавляющее большинство разработчиков не имеют понятия о том, как создавать защищенные криптографические системы. – Joey

1

Вам нужна отметка времени даты в имени файла?

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

+0

@downvoter Любая причина для голосования? GUID для имени файла, по-видимому, является популярным ответом на этот вопрос. –

+0

Это повторный ответ, и у меня нет достаточной репутации для downvote –

+0

@XMLforDummies, мой ответ был одним из первых. Сейчас это может показаться не таким, потому что сейчас просто показывают часы. Это повторный ответ, потому что это, вероятно, правильный ответ. –

82

Использование

Path.GetTempFileName() 

или использовать новый GUID().

Path.GetTempFilename() on MSDN.

+0

Вот ссылка на документ MSDN: http://msdn.microsoft.com/en-us/library/system.io.path.gettempfilename.aspx – epotter

+3

Обратите внимание, что это 'GetTempFileName () 'может вызывать исключение, если вы создадите много таких файлов, не удаляя их. – Joey

+17

"Метод GetTempFileName поднимет [IOException] (http://msdn.microsoft.com/en-us/library/system.io.ioexception.aspx), если он используется для создания более 65535 файлов без удаления предыдущего временного файлы «. говорится в статье MSDN. –

1

Как с помощью Guid.NewGuid() создать GUID и использовать его как имя файла (или часть имени файла вместе с меткой времени, если хотите).

3
  1. Создать свой датируемые имя файла после вашего нормального процесса
  2. Проверьте, если файл существует
  3. Ложные - сохранить файл
  4. True - Добавляет дополнительный символ в файл, возможно, счетчик
  5. Перейти к Этап 2
+10

Этот алгоритм доступен для параллелизма –

+0

См. Мой ответ на алгоритм, который является потокобезопасным. –

0

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

44

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

Например:

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

filename1 = GenerateFileName("MeasurementData"); 
filename2 = GenerateFileName("Image"); 

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

Обратите внимание, что ограничение на имя файла в окнах составляет 255 символов.

+1

+1 Для предложения включить полезную информацию * в сочетании с * GUID. - В стороне, чтобы взять с солью соли: включая дату и время в имени файла, является излишним, когда вы можете просто «Щелкните правой кнопкой мыши> Сортировать по> Дате». –

+1

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

+0

Должен быть 'Guid.NewGuid(). ToString();'. Отсутствует скобка. +1 в противном случае –

0

я обычно делаю что-то вдоль этих линий:

  • старт с именем файла (стволовой work.dat1, например)
  • попытаться создать его с CreateNew
  • , если это работает, вы получили файл, в противном случае ...
  • перемешайте текущую дату/время в имя файла (например, work.2011-01-15T112357.dat)
  • попытки создать файл
  • Если это сработало, у вас есть файл, иначе ...
  • Смешайте монотонный счетчик в имени файла (например, work.2011-01-15T112357.0001.dat. (Мне не нравятся GUID. Я предпочитаю порядок/предсказуемость.)
  • попытаться создать файл. Продолжайте пометить счетчик и повторите попытку, пока файл не будет создан для вас.

Вот класс образец:

static class DirectoryInfoHelpers 
{ 
    public static FileStream CreateFileWithUniqueName(this DirectoryInfo dir , string rootName) 
    { 
     FileStream fs = dir.TryCreateFile(rootName) ; // try the simple name first 

     // if that didn't work, try mixing in the date/time 
     if (fs == null) 
     { 
      string date = DateTime.Now.ToString("yyyy-MM-ddTHHmmss") ; 
      string stem = Path.GetFileNameWithoutExtension(rootName) ; 
      string ext = Path.GetExtension(rootName) ?? ".dat" ; 

      ext = ext.Substring(1); 

      string fn = string.Format("{0}.{1}.{2}" , stem , date , ext) ; 
      fs = dir.TryCreateFile(fn) ; 

      // if mixing in the date/time didn't work, try a sequential search 
      if (fs == null) 
      { 
       int seq = 0 ; 
       do 
       { 
        fn = string.Format("{0}.{1}.{2:0000}.{3}" , stem , date , ++seq , ext) ; 
        fs = dir.TryCreateFile(fn) ; 
       } while (fs == null) ; 
      } 

     } 

     return fs ; 
    } 

    private static FileStream TryCreateFile(this DirectoryInfo dir , string fileName) 
    { 
     FileStream fs = null ; 
     try 
     { 
      string fqn = Path.Combine(dir.FullName , fileName) ; 

      fs = new FileStream(fqn , FileMode.CreateNew , FileAccess.ReadWrite , FileShare.None) ; 
     } 
     catch (Exception) 
     { 
      fs = null ; 
     } 
     return fs ; 
    } 

} 

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

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

+0

*** Потоковая защита ***? – Kiquenet

0

Я заканчивает конкатенации с GUID День Месяц Год Второй миллисекунду строки и я думаю, что это решение довольно хорошо в моем сценарии

+1

какой-либо конечный исходный код для улучшения ответа? – Kiquenet

18

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

Например, если вы передаете его test.txt, он будет пытаться создавать файлы в таком порядке:

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

и т.д. Вы можете указать максимальное количество попыток или просто оставить его по умолчанию.

Вот полный пример:

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<string>(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("Could not create unique filename in " + maxAttempts + " attempts"); 
    } 

    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

*** потокобезопасный ***? не 'static readonly' _variable_ ни' lock'? – Kiquenet

+0

Сам метод статический, поэтому ничего не имеет, поэтому я считаю, что несколько потоков могут безопасно вводить этот метод одновременно. Возможно, потокобезопасность - не совсем правильный термин - я пытаюсь сказать, что если другой поток/процесс создает файл с конфликтующим именем во время выполнения, этот метод восстанавливает и пытается использовать следующее доступное имя. Не стесняйтесь редактировать, если считаете, что его можно улучшить. –

+0

Может быть, «не страдая от состояния гонки» - это лучший способ поместить его. –

0

вы можете использовать Random.Next() также для генерации случайного числа. вы можете увидеть ссылку MSDN: http://msdn.microsoft.com/en-us/library/9b3ta19y.aspx

+1

Я бы не рекомендовал это, так как это не обязательно быть случайным. Он должен быть уникальным. – Max

2

Я написал простую рекурсивную функцию, которая генерирует имена файлов, такие как Windows, путем добавления порядкового номера до расширения файла.

Учитывая желаемый путь к файлу C:\MyDir\MyFile.txt, и файл уже существует, он возвращает конечный путь к файлу C:\MyDir\MyFile_1.txt.

Это называется так:

var desiredPath = @"C:\MyDir\MyFile.txt"; 
var finalPath = UniqueFileName(desiredPath); 

private static string UniqueFileName(string path, int count = 0) 
{ 
    if (count == 0) 
    { 
     if (!File.Exists(path)) 
     { 
      return path; 
     } 
    } 
    else 
    { 
     var candidatePath = string.Format(
      @"{0}\{1}_{2}{3}", 
      Path.GetDirectoryName(path), 
      Path.GetFileNameWithoutExtension(path), 
      count, 
      Path.GetExtension(path)); 

     if (!File.Exists(candidatePath)) 
     { 
      return candidatePath; 
     } 
    } 

    count++; 
    return UniqueFileName(path, count); 
} 
+0

является «исходным кодом» ** потокобезопасным **? – Kiquenet

2

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

Начну с уникальным именем, используя временную метку -

"context_" + DateTime.Now.ToString ("yyyyMMddHHmmssffff")

C# код -

public static string CreateUniqueFile(string logFilePath, string logFileName, string fileExt) 
    { 
     try 
     { 
      int fileNumber = 1; 

      //prefix with . if not already provided 
      fileExt = (!fileExt.StartsWith(".")) ? "." + fileExt : fileExt; 

      //Generate new name 
      while (File.Exists(Path.Combine(logFilePath, logFileName + "-" + fileNumber.ToString() + fileExt))) 
       fileNumber++; 

      //Create empty file, retry until one is created 
      while (!CreateNewLogfile(logFilePath, logFileName + "-" + fileNumber.ToString() + fileExt)) 
       fileNumber++; 

      return logFileName + "-" + fileNumber.ToString() + fileExt; 
     } 
     catch (Exception) 
     { 
      throw; 
     } 
    } 

    private static bool CreateNewLogfile(string logFilePath, string logFile) 
    { 
     try 
     { 
      FileStream fs = new FileStream(Path.Combine(logFilePath, logFile), FileMode.CreateNew); 
      fs.Close(); 
      return true; 
     } 
     catch (IOException) //File exists, can not create new 
     { 
      return false; 
     } 
     catch (Exception)  //Exception occured 
     { 
      throw; 
     } 
    } 
2

Я использую это :

 public static string GenerateFileName(string extension="") 
     { 
      return string.Concat(Path.GetRandomFileName().Replace(".", ""), 
       (!string.IsNullOrEmpty(extension)) ? (extension.StartsWith(".") ? extension : string.Concat(".", extension)) : ""); 
     } 
1

Почему мы не можем сделать уникальный идентификатор, как показано ниже.

Мы можем использовать DateTime.Now.Ticks и Guid.NewGuid(). ToString() для объединения и создания уникального идентификатора.

По мере добавления DateTime.Now.Ticks мы можем узнать дату и время в секундах, в которых создан уникальный идентификатор.

См. Код.

var ticks = DateTime.Now.Ticks; 
var guid = Guid.NewGuid().ToString(); 
var uniqueSessionId = ticks.ToString() +'-'+ guid; //guid created by combining ticks and guid 

var datetime = new DateTime(ticks);//for checking purpose 
var datetimenow = DateTime.Now; //both these date times are different. 

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

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

0

У вас может быть уникальное имя файла, автоматически созданное для вас без каких-либо специальных методов. Просто используйте следующее с StorageFolder Class или StorageFile Class. Ключевым моментом здесь является: CreationCollisionOption.GenerateUniqueName и NameCollisionOption.GenerateUniqueName

Чтобы создать новый файл с уникальным именем:

var myFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("myfile.txt", NameCollisionOption.GenerateUniqueName); 

К копия файл в папку с уникальным именем:

var myFile2 = await myFile1.CopyAsync(ApplicationData.Current.LocalFolder, myFile1.Name, NameCollisionOption.GenerateUniqueName); 

К переместить файл с уникальным именем файла в целевом местоположении:

await myFile.MoveAsync(ApplicationData.Current.LocalFolder, myFile.Name, NameCollisionOption.GenerateUniqueName); 

Чтобы переименовать файл с уникальным именем в месте назначения:

await myFile.RenameAsync(myFile.Name, NameCollisionOption.GenerateUniqueName); 
Смежные вопросы