Как создать уникальные имена файлов в C#
я реализовал алгоритм, который будет генерировать уникальные имена для файлов, которые будет сохранить на жестком диске. Я добавляю DateTime:часы,минуты,секунды и миллисекунды но все же он генерирует повторяющиеся имена файлов, потому что я загружаю несколько файлов одновременно.
каково лучшее решение для создания уникальных имен для файлов, которые будут храниться на жестком диске, чтобы никакие 2 файла не были одинаковыми?
17 ответов:
если читаемость не имеет значения, используйте GUIDs.
например:
var myUniqueFileName = string.Format(@"{0}.txt", Guid.NewGuid());или короче:
var myUniqueFileName = $@"{Guid.NewGuid()}.txt";в моих программах я иногда пытаюсь, например, 10 раз создать читаемое имя ("Image1.формат PNG."."Image10.png") и если это не удается (потому что файл уже существует), я возвращаюсь к GUID.
обновление:
недавно я также использовал
DateTime.Now.Ticksвместо Идентификаторы GUID:var myUniqueFileName = string.Format(@"{0}.txt", DateTime.Now.Ticks);или
var myUniqueFileName = $@"{DateTime.Now.Ticks}.txt";преимущество для меня заключается в том, что это создает более короткое и "более приятное" имя файла, по сравнению с GUID.
обратите внимание, что в некоторых случаях (например, при создании большого количества случайных имен за очень короткое время), это может сделать не уникальные значения.
придерживайтесь GUID, если вы хотите действительно убедиться, что имена файлов уникальны, даже при переносе их на другие компьютеры.
Если читаемость имени файла не важна, то GUID, как это предлагают многие, будет делать. Однако, я считаю, что глядя в каталог с 1000 наименований идентификатор файла является очень сложной, чтобы разложить по полочкам. Поэтому я обычно использую комбинацию статической строки, которая дает имя файла некоторую контекстную информацию, метку времени и GUID.
например:
public string GenerateFileName(string context) { return context + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + Guid.NewGuid().ToString("N"); } filename1 = GenerateFileName("MeasurementData"); filename2 = GenerateFileName("Image");таким образом, когда я сортирую по имени файла, он автоматически группирует файлы по строке контекста и Сортировать по времени.
обратите внимание, что ограничение имени файла в windows составляет 255 символов.
вот алгоритм, который возвращает уникальное читаемое имя файла на основе предоставленного оригинала. Если исходный файл существует, он постепенно пытается добавить индекс к имени файла, пока не найдет тот, который не существует. Он считывает существующие имена файлов в хэш-набор для проверки коллизий, поэтому он довольно быстрый (несколько сотен имен файлов в секунду на моей машине), он также потокобезопасен и не страдает от условий гонки.
например, если вы передаете его
test.txt, это попытаемся создать файлы в следующем порядке:test.txt test (2).txt test (3).txtetc. Вы можете указать максимальное количество попыток или просто оставить его по умолчанию.
вот пример:
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(); } }
Я использую GetRandomFileName:
метод GetRandomFileName возвращает криптографически сильную случайную строку, которая может использоваться как имя папки или имя файла. В отличие от GetTempFileName, GetRandomFileName не создает файл. Когда безопасность вашей файловой системы имеет первостепенное значение, этот метод следует использовать вместо GetTempFileName.
пример:
public static string GenerateFileName(string extension="") { return string.Concat(Path.GetRandomFileName().Replace(".", ""), (!string.IsNullOrEmpty(extension)) ? (extension.StartsWith(".") ? extension : string.Concat(".", extension)) : ""); }
- создать имя файла с отметкой времени после вашего нормального процесса
- проверьте, существует ли имя файла
- False-сохранить файл
- True-добавить дополнительный символ в файл, возможно, счетчик
- перейти к Шагу 2
Я использую следующий код и работает нормально. Я надеюсь, что это может помочь вам.
Я начинаю с уникального имени файла, используя метку времени -
"context_" + DateTime.Сейчас.ToString ("yyyyMMddHHmmssffff")
C# code -
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; } }
вы можете иметь уникальное имя файл автоматически без каких-либо специальных методов. Просто используйте следующее с Класс StorageFolder или Класс StorageFile. Ключ здесь:
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);
Как насчет использования
Guid.NewGuid()чтобы создать GUID и использовать его в качестве имени файла (или часть имени файла вместе с вашей меткой времени, если хотите).
Я написал простую рекурсивную функцию, которая генерирует имена файлов как в 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); }
почему мы не можем сделать уникальный идентификатор, как показано ниже.
мы можем использовать DateTime.Сейчас.Клещи и Guid.NewGuid ().ToString () для объединения и создания уникального идентификатора.
как дата и время.Сейчас.Тики добавляется, мы можем узнать дату и время в секундах, в которых создается уникальный идентификатор.
пожалуйста, смотрите код.
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 и инкремент, когда вы создали файл. Таким образом, имя файла, безусловно, будет уникальным, так как у вас есть секунды также в файле.
Я обычно делаю что-то в этом роде:
- начните с имени файла stem (
work.dat1например)- попробуйте создать его с помощью CreateNew
- если это работает, у вас есть файл, в противном случае...
- смешайте текущую дату / время в имени файла (
work.2011-01-15T112357.datнапример)- попробуйте создать файл
- если это сработало, у вас есть файл, в противном случае...
- смешайте монотонный счетчик в имя файла (
work.2011-01-15T112357.0001.datнапример. (Я не люблю GUIDs. Я предпочитаю порядок / предсказуемость.)- попробуйте создать файл. Продолжайте отмечать счетчик и повторять попытку, пока файл не будет создан для вас.
вот пример класса:
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 ; } }вы можете настроить алгоритм (всегда используйте все возможные компоненты для имени файла, например). Зависит от контекста -- если бы я создавал файлы журналов, например, которые я мог бы захотеть повернуть из существования, вы бы хотите, чтобы все они использовали один и тот же шаблон для имени.
код не идеален (нет проверки данных, переданных, например). И алгоритм не идеален (если вы заполняете жесткий диск или сталкиваетесь с разрешениями, фактическими ошибками ввода-вывода или другими ошибками файловой системы, например, это будет висеть, как он стоит, в бесконечном цикле).
Я заканчиваю конкатенацию GUID С днем месяца года второй миллисекундной строкой, и я думаю, что это решение довольно хорошо в моем сценарии
вы можете использовать случайный.Далее () также для генерации случайного числа. вы можете увидеть ссылку MSDN:http://msdn.microsoft.com/en-us/library/9b3ta19y.aspx
Comments