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

时间:2011-01-11 13:14:32

标签: c#

我已经实现了一种算法,该算法将为将保存在硬盘上的文件生成唯一的名称。我正在追加DateTime小时,分钟,秒和毫秒,但它仍会生成重复的文件名,因为我一次只能上传多个文件。

为要存储在硬盘驱动器上的文件生成唯一名称的最佳解决方案是什么,所以没有2个文件相同?

20 个答案:

答案 0 :(得分:217)

如果可读性无关紧要,请使用GUIDs

E.g:

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

shorter

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。

答案 1 :(得分:86)

使用

Path.GetTempFileName()

或使用新的GUID()。

Path.GetTempFilename() on MSDN

答案 2 :(得分:71)

System.IO.Path.GetRandomFileName()

Path.GetRandomFileName() on MSDN

答案 3 :(得分:49)

如果文件名的可读性不重要,那么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个字符。

答案 4 :(得分:19)

这是一种算法,它根据提供的原始文件返回唯一可读的文件名。如果原始文件存在,则会逐渐尝试将索引附加到文件名,直到找到不存在的文件名。它将现有的文件名读入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();
    }
}

答案 5 :(得分:5)

我使用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)) : "");
}

答案 6 :(得分:3)

  1. 创建带时间戳的文件名 遵循正常流程
  2. 检查文件名是否存在
  3. 错误 - 保存文件
  4. True - 将附加字符附加到文件,可能是计数器
  5. 转到第2步

答案 7 :(得分: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;
        }
    }

答案 8 :(得分:2)

您可以自动为您生成一个唯一的文件名,而无需任何自定义方法。只需对StorageFolder ClassStorageFile Class使用以下内容即可。这里的关键是:CreationCollisionOption.GenerateUniqueNameNameCollisionOption.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);

答案 9 :(得分:1)

如何使用Guid.NewGuid()创建GUID并将其用作文件名(如果您愿意,可以将文件名的一部分与时间戳一起使用)。

答案 10 :(得分:1)

您是否需要文件名中的日期时间戳?

您可以将文件名设为GUID。

答案 11 :(得分:1)

我编写了一个简单的递归函数,通过在文件扩展名之前附加序列号来生成像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);
}

答案 12 :(得分:1)

为什么我们不能在下面创建一个唯一的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。

答案 13 :(得分:0)

如果您想要日期时间,小时,分钟等,您可以使用静态变量。将此变量的值附加到文件名。您可以使用0启动计数器,并在创建文件时递增。这样,文件名肯定是唯一的,因为文件中也有秒数。

答案 14 :(得分: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 ;
    }

}

您可能想要调整算法(例如,始终使用所有可能的组件到文件名)。取决于上下文 - 例如,如果我创建日志文件,我可能想要旋转不存在,您希望它们都与名称共享相同的模式。

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

答案 15 :(得分:0)

我最终将GUID与Day Month Year Second Millisecond字符串连接起来,我觉得这个解决方案在我的场景中相当不错

答案 16 :(得分:0)

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

答案 17 :(得分:0)

我为此专门编写了一个类。它使用“基本”部分进行初始化(默认为精确到分钟的时间戳),之后再附加字母以组成唯一的名称。因此,如果生成的第一个邮票是1907101215a,则第二个邮票是1907101215b,然后是1907101215c,等等。

如果我需要超过25张独特的邮票,那么我会使用一元'z'来计数25个。因此,它进入了1907101215y,1907101215za,1907101215zb,... 1907101215zy,1907101215zza,1907101215zzb等。这样可以保证图章始终按字母数字顺序生成(只要图章后面的下一个字符不是字母)即可。

它不是线程安全的,不会自动更新时间,并且如果您需要数百个邮票会迅速膨胀,但是我发现它足以满足我的需要。

module.exports = (filename, opts = {}) => {
                                 ^

SyntaxError: Unexpected token =
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:374:25)

Starting Jest in Watch mode failed too many times and has been stopped.
 see troubleshooting: https://github.com/jest-community/vscode-jest/blob/master/README.md#troubleshooting

答案 18 :(得分:0)

DateTime.Now.Ticks并不安全,Guid.NewGuid()太丑陋,如果您需要干净且几乎安全的物品(例如,不是100%安全的话, 1ms内完成1,000,000次),请尝试:

Math.Abs(Guid.NewGuid().GetHashCode())

通过安全,我的意思是当您在很短的时间内(几毫秒)多次调用它时,它是唯一的。

答案 19 :(得分:0)

老问题,我知道,但这对我有用。如果多个线程下载文件,则为每个线程分配一个唯一编号并在其前面添加文件名,例如01_202107210938xxxx