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