如何在C#中生成唯一的文件名

我已经实现了一个algorithm,将生成将保存在硬盘上的文件的唯一名称。 我附加DateTime 时间小时,分钟,秒和毫秒,但它仍然会生成重复的名称的文件,因为一次上传多个文件。

什么是最好的解决scheme,以生成独特的文件名称存储在硬盘驱动器,所以没有2个文件是相同的?

如果可读性不重要,请使用GUID 。

例如:

 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。

使用

 Path.GetTempFileName() 

或者使用新的GUID()。

Path.GetTempFilename()在MSDN上 。

 System.IO.Path.GetRandomFileName() 

MSDN上的Path.GetRandomFileName() 。

如果文件名的可读性不重要,那么许多人build议的GUID将会这样做。 但是,我发现查看1000个GUID文件名的目录是非常困难的。 所以我通常使用一个静态string的组合,它给文件名称一些上下文信息,时间戳和GUID。

例如:

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

这样,当我按文件名sorting时,它将自动按上下文string对文件进行分组,并按时间戳sorting。

请注意,窗口中的文件名限制是255个字符。

这是一个algorithm,返回一个独特的可读文件名,基于提供的原始文件。 如果原始文件存在,它会逐渐尝试追加一个索引到文件名,直到find一个不存在的文件。 它将现有的文件名读入HashSet来检查冲突,所以它非常快(我的机器上每秒钟有几百个文件名),它也是线程安全的,不会受到竞争条件的影响。

例如,如果你传递了test.txt ,它将尝试按以下顺序创build文件:

 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(); } } 
  1. 按照正常stream程创build您的时间戳文件
  2. 检查是否存在文件名
  3. 假 – 保存文件
  4. 真 – 附加额外的字符文件,也许是一个计数器
  5. 转到第2步

我已经写了一个简单的recursion函数,通过在文件扩展名之前附加一个序列号来生成像Windows一样的文件名。

给定一个所需的文件pathC:\MyDir\MyFile.txt ,并且该文件已经存在,它将返回C:\MyDir\MyFile_1.txt的最终文件path。

这是这样调用的:

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

我一直在使用下面的代码,它的工作正常。 我希望这可以帮助你。

我使用一个时间戳开始一个独特的文件名 –

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

你需要文件名中的date时间戳吗?

您可以使文件名GUID。

如何使用Guid.NewGuid()来创build一个GUID并使用它作为文件名(或者如果你喜欢,可以和你的时间戳一起使用文件名的一部分)。

我使用这个:

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

为什么我们不能如下创build一个唯一的ID。

我们可以使用DateTime.Now.Ticks和Guid.NewGuid()。ToString()来组合一个唯一的ID。

当添加DateTime.Now.Ticks时,我们可以在几秒钟内find创build唯一标识的date和时间。

请参阅代码。

 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. 

我们甚至可以采取独特的编号的部分蜱和日后检查date和时间,以备将来参考。

您可以将创build的唯一ID附加到文件名,也可以用于创build用于login用户的唯一会话ID到我们的应用程序或网站。

如果你想有date时间,小时,分钟等..你可以使用一个静态variables。 追加这个variables的值到文件名。 您可以用0启动计数器,并在创build文件时增加。 这样,文件名肯定是唯一的,因为你在文件中也有几秒钟的时间。

我通常会按照这些方法做一些事情:

  • 从干文件名开始(例如work.dat1
  • 尝试使用CreateNew创build它
  • 如果这样做,你已经得到的文件,否则…
  • 将当前date/时间混合到文件名中(例如work.2011-01-15T112357.dat
  • 尝试创build该文件
  • 如果工作,你有文件,否则…
  • 混合一个单调的计数器到文件名(例如work.2011-01-15T112357.0001.dat (我不喜欢GUID,我更喜欢顺序/可预测性。)
  • 尝试创build该文件。 继续打勾计数器并重试,直到为您创build一个文件。

这是一个示例类:

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

您可能想要调整algorithm(例如,总是使用所有可能的组件到文件名)。 取决于上下文 – 如果我正在创build日志文件作为例子,我可能想旋转存在,你会希望他们都分享相同的模式的名称。

代码并不完美(例如没有传入数据的检查)。 algorithm并不完美(如果你填满硬盘或遇到权限,实际I / O错误或其他文件系统错误,例如,这将挂起,因为它站在一个无限循环)。

我最终连接GUID与日月二毫秒string,我认为这个解决scheme是相当不错的,在我的情况

你可以使用Random.Next()来产生一个随机数。 你可以看到MSDN链接: http : //msdn.microsoft.com/en-us/library/9b3ta19y.aspx