我已经实现了一种算法,该算法将为要保存在硬盘上的文件生成唯一的名称。我要附加DateTime:小时,分钟,秒和毫秒,但仍会生成重复的文件名,因为我一次上传了多个文件。
为要存储在硬盘驱动器上的文件生成唯一名称从而没有两个文件相同的最佳解决方案是什么?
我已经实现了一种算法,该算法将为要保存在硬盘上的文件生成唯一的名称。我要附加DateTime:小时,分钟,秒和毫秒,但仍会生成重复的文件名,因为我一次上传了多个文件。
为要存储在硬盘驱动器上的文件生成唯一名称从而没有两个文件相同的最佳解决方案是什么?
Answers:
如果可读性无关紧要,请使用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,即使将其传输到其他计算机时也是如此。
DateTime.Now.Ticks.GetHashCode().ToString("x").ToUpper()
GetTempFileName()如果您创建许多此类文件而不删除它们,则可能会引发异常。
GetTempFileName将创建一个文件。这也意味着它选择了临时路径位置。另一方面,GetRandomFileName适用于生成可与其他路径一起使用的8.3 文件名。(我看过一些使用GetTempFileName和File.Delete只是在其他地方使用文件名的可怕代码。)
System.IO.Path.GetRandomFileName()
如果文件名的可读性不重要,那么许多人建议使用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");
这样,当我按文件名排序时,它将自动按上下文字符串将文件分组并按时间戳排序。
请注意,Windows中的文件名限制为255个字符。
Right Click > Sort By > Date。
Guid.NewGuid().ToString();。缺少括号。+1否则
这是一种算法,该算法根据提供的原始文件返回唯一的可读文件名。如果原始文件存在,它将以增量方式尝试将索引添加到文件名,直到找到不存在的索引。它将现有的文件名读取到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();
}
}
static readonly 变也不lock?
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)) : "");
}
您可以为您自动生成一个唯一的文件名,而无需任何自定义方法。只需将以下内容与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);
我一直在使用以下代码及其正常工作。希望对您有所帮助。
我首先使用时间戳来指定唯一的文件名-
“ 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;
}
}
您是否需要文件名中的日期时间戳?
您可以将文件名设为GUID。
如何使用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);
}
我们为什么不能按以下方式创建唯一的ID。
我们可以使用DateTime.Now.Ticks和Guid.NewGuid()。ToString()组合在一起并创建唯一的ID。
添加DateTime.Now.Ticks后,我们可以找到创建唯一ID的日期和时间(以秒为单位)。
请查看代码。
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.
我们甚至可以使用唯一ID中的刻度线部分,并在以后检查日期和时间以供将来参考。
您可以将创建的唯一ID附加到文件名中,或者可以用于创建唯一的会话ID,以便用户登录到我们的应用程序或网站。
Guid.NewGuid(忽略该事实(在某些情况下可能不是有趣的事实),我们就可以断言,如果我们有足够高的概率不关心其他情况,则唯一ID将被生成-这比“滴答声”要高得多。因此,“滴答”没有任何价值/用途,因为“次要”数据被推入文件名。
我通常按照以下方式做一些事情:
work.dat1例如)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 ;
}
}
您可能需要调整算法(例如,始终使用所有可能的组件作为文件名)。取决于上下文-例如,如果我正在创建日志文件,我可能想轮换使用,那么您希望它们全部共享与该名称相同的模式。
代码不是完美的(例如,不检查传入的数据)。而且该算法也不是完美的(例如,如果您填满硬盘驱动器或遇到权限,实际的I / O错误或其他文件系统错误,则这种情况将无限期地挂起)。
我最终将GUID与Day Month Year Second Millisecond字符串连接起来,我认为这种解决方案在我的情况下非常好
您还可以使用Random.Next()生成随机数。您可以看到MSDN链接:http : //msdn.microsoft.com/en-us/library/9b3ta19y.aspx
我为此专门编写了一个类。它使用“基本”部分进行初始化(默认为精确到分钟的时间戳),之后再附加字母以组成唯一的名称。因此,如果生成的第一个戳记是1907101215a,则第二个戳记是1907101215b,然后是1907101215c等。
如果我需要超过25张独特的邮票,那么我会使用一元“ z”来计数25个。因此,它进入1907101215y,1907101215za,1907101215zb,... 1907101215zy,1907101215zza,1907101215zzb等,依此类推。这保证了图章将始终按其生成顺序按字母数字顺序排序(只要图章后面的下一个字符不是字母)。
它不是线程安全的,不会自动更新时间,并且在需要数百个邮票时会很快膨胀,但是我发现它足以满足我的需要。
/// <summary>
/// Class for generating unique stamps (for filenames, etc.)
/// </summary>
/// <remarks>
/// Each time ToString() is called, a unique stamp is generated.
/// Stamps are guaranteed to sort alphanumerically in order of generation.
/// </remarks>
public class StampGenerator
{
/// <summary>
/// All the characters which could be the last character in the stamp.
/// </summary>
private static readonly char[] _trailingChars =
{
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y'
};
/// <summary>
/// How many valid trailing characters there are.
/// </summary>
/// <remarks>Should always equal _trailingChars.Length</remarks>
public const int TRAILING_RANGE = 25;
/// <summary>
/// Maximum length of the stamp. Hard-coded for laziness.
/// </summary>
public const int MAX_LENGTH_STAMP = 28;
/// <summary>
/// Base portion of the stamp. Will be constant between calls.
/// </summary>
/// <remarks>
/// This is intended to uniquely distinguish between instances.
/// Default behavior is to generate a minute-accurate timestamp.
/// </remarks>
public string StampBase { get; }
/// <summary>
/// Number of times this instance has been called.
/// </summary>
public int CalledTimes { get; private set; }
/// <summary>
/// Maximum number of stamps that can be generated with a given base.
/// </summary>
public int MaxCalls { get; }
/// <summary>
/// Number of stamps remaining for this instance.
/// </summary>
public int RemainingCalls { get { return MaxCalls - CalledTimes; } }
/// <summary>
/// Instantiate a StampGenerator with a specific base.
/// </summary>
/// <param name="stampBase">Base of stamp.</param>
/// <param name="calledTimes">
/// Number of times this base has already been used.
/// </param>
public StampGenerator(string stampBase, int calledTimes = 0)
{
if (stampBase == null)
{
throw new ArgumentNullException("stampBase");
}
else if (Regex.IsMatch(stampBase, "[^a-zA-Z_0-9 \\-]"))
{
throw new ArgumentException("Invalid characters in Stamp Base.",
"stampBase");
}
else if (stampBase.Length >= MAX_LENGTH_STAMP - 1)
{
throw new ArgumentException(
string.Format("Stamp Base too long. (Length {0} out of {1})",
stampBase.Length, MAX_LENGTH_STAMP - 1), "stampBase");
}
else if (calledTimes < 0)
{
throw new ArgumentOutOfRangeException(
"calledTimes", calledTimes, "calledTimes cannot be negative.");
}
else
{
int maxCalls = TRAILING_RANGE * (MAX_LENGTH_STAMP - stampBase.Length);
if (calledTimes >= maxCalls)
{
throw new ArgumentOutOfRangeException(
"calledTimes", calledTimes, string.Format(
"Called Times too large; max for stem of length {0} is {1}.",
stampBase.Length, maxCalls));
}
else
{
StampBase = stampBase;
CalledTimes = calledTimes;
MaxCalls = maxCalls;
}
}
}
/// <summary>
/// Instantiate a StampGenerator with default base string based on time.
/// </summary>
public StampGenerator() : this(DateTime.Now.ToString("yMMddHHmm")) { }
/// <summary>
/// Generate a unique stamp.
/// </summary>
/// <remarks>
/// Stamp values are orered like this:
/// a, b, ... x, y, za, zb, ... zx, zy, zza, zzb, ...
/// </remarks>
/// <returns>A unique stamp.</returns>
public override string ToString()
{
int zCount = CalledTimes / TRAILING_RANGE;
int trailing = CalledTimes % TRAILING_RANGE;
int length = StampBase.Length + zCount + 1;
if (length > MAX_LENGTH_STAMP)
{
throw new InvalidOperationException(
"Stamp length overflown! Cannot generate new stamps.");
}
else
{
CalledTimes = CalledTimes + 1;
var builder = new StringBuilder(StampBase, length);
builder.Append('z', zCount);
builder.Append(_trailingChars[trailing]);
return builder.ToString();
}
}
}
DateTime.Now.Ticks不安全,Guid.NewGuid()太丑陋,如果您需要清洁且几乎安全的物品(例如,如果在1ms内调用1,000,000次,则不是100%安全),请尝试:
Math.Abs(Guid.NewGuid().GetHashCode())
保险柜是指您在短短几毫秒的时间内多次调用保险柜时就变得独一无二。
GetHashCode方法返回int范围为32位的,而范围GUID为128位的,因此更有可能是唯一的。如果您不喜欢GUID值的格式,只需调用ToString("N")它即可删除破折号。