【问题标题】:C#: What is the fastest way to generate a unique filename?C#:生成唯一文件名的最快方法是什么?
【发布时间】:2010-12-08 19:45:56
【问题描述】:

我看到了一些关于随机命名文件的建议,包括使用

System.IO.Path.GetRandomFileName()

或使用

System.Guid

并附加文件扩展名。

我的问题是:生成唯一文件名的最快方法是什么?

【问题讨论】:

  • 你为什么不使用一种足够快的方式?
  • 一个很好的问题。答案是我试图从这个文件系统密集型应用程序中榨取每一盎司的性能。
  • 这真的取决于你所说的“唯一”是什么意思——它是普遍唯一的,在文件系统中是唯一的,在同一程序的不同线程中是唯一的......?
  • @Anders:好点子。我已经有一个文件夹路径——所以我的意思是“在给定的文件夹中是唯一的”
  • 你几乎肯定不在乎。假设您要对该随机文件名执行任何 IO,这将使创建文件名所花费的时间相形见绌。集中优化可以发挥作用的地方。

标签: c# .net windows performance filesystems


【解决方案1】:

GUID 会非常快,因为它的实现保证 Windows 可以在 100 纳秒的时间跨度内生成至少 16,384 个 GUID。 (正如其他人指出的那样,规范并不能保证,只允许。但是,GUID 的生成真的非常非常快。真的。)任何网络上任何位置的任何文件系统发生冲突的可能性都非常低。这很安全,尽管最好的做法是始终检查该文件名是否可用,但实际上您甚至不需要这样做。

因此,除了保存本身之外,您没有看到任何 I/O 操作,并且生成名称本身需要

【讨论】:

  • 哇!您是否有描述此实施保证的链接源?
  • 我不相信 Windows 保证它可以在 100 nS 内生成 16,384 个 GUIDS - 只是保证由 GUID 算法产生的值具有这种分布水平。此外,使用逻辑,每 100-Ns 16k GUIDS 将是每秒 1630 亿 GUIDS ......这意味着生成 GUID 需要不到 1/10 的 CPU 周期?这没有多大意义。
  • 查看这个关于 GUID 唯一性的问题 stackoverflow.com/questions/39771/…
  • @Dan Raymond Chen 在这里分解了 GUID 的组成部分(它不仅仅是一个随机数):blogs.msdn.com/oldnewthing/archive/2008/06/27/8659071.aspx 注意 14 位唯一标识符,它确保 GUID 在相同的 100 纳秒内生成时间跨度仍然是 2^14 分之一的唯一性。
  • @LBushkin 你是对的,这是对规范的糟糕解读。但是,我坚持这样的说法,即生成 GUID 是如此愚蠢,您无需费心测量它。
【解决方案2】:

你想要System.IO.Path.GetTempFileName()

我实际上不能说它是否最快,但这是正确的方法,这更重要。

【讨论】:

  • 这实际上创建了一个文件。我想你的意思是 System.IO.Path.GetRandomFileName()
  • 这有三个问题:1) 无法控制文件扩展名,它总是 .TMP,2) 无法控制创建临时文件的目录,它总是在 TEMP,3) 它使用 P /Invoke 让操作系统生成临时文件,因此它几乎肯定不是 最快 方法,这正是 OP 似乎正在寻找的方法。
  • meh:对名称或路径的控制不是要求的一部分。谁说 P/Invoke 一定很慢?
  • 对于使用 GetTempFileName(在 System.IO.Path 或 Win32 调用中)的任何人,您可能需要阅读以下文章以了解此系统的弱点:dotnet.org.za/markn/archive/2006/04/15/51594.aspx 此外,它可能不是最快的方式。由于需要编组,外部调用 很慢。制作新的 Guid 涉及对运行时的内部调用。我想只是将调用传递给相同的 Win32 API(CoCreateGuid 或 UUIDCreate)......
  • 请注意GetRandomFileName() 可能会创建带有您可能无法删除的扩展名的文件。例如,我遇到了扩展名为“msp”的文件的问题,一旦我不再需要临时文件,我就无法删除这些文件。
【解决方案3】:

好吧,我已经编写文件系统驱动程序 20 年了,我会说 Rex 是正确的。生成 guid 的速度要快得多,因为与搜索唯一文件名相比,它需要的开销要少得多。 GetTempFileName 实际上创建了一个文件,这意味着它必须调用整个文件系统驱动程序堆栈(谁知道会调用多少次并切换到内核模式。) GetRandomFileName 听起来更快,但是我相信 GUID 调用更快。人们没有意识到的是,即使测试文件是否存在也需要通过驱动程序堆栈进行完整调用。它实际上会导致打开、获取属性和关闭(这至少是 3 次调用,具体取决于级别)。实际上,它至少是 20 次函数调用并转换到内核模式。 GUIDS 保证唯一性对于大多数用途来说已经足够了。

我的建议是仅在文件不存在时生成名称并创建文件。如果是,则抛出异常并捕获它,然后生成新的 guid 并重试。这样一来,您出错的可能性为零,并且可以在晚上轻松入睡。

顺便说一句,检查错误太过分了。如果假设错误,代码应该被设计成崩溃,或者捕获异常并在那时处理它。在异常堆栈上推送、弹出和寻址比每次都检查每个函数是否有错误要快得多。

【讨论】:

    【解决方案4】:

    如果您控制文件所在的目的地,并且只有一个进程和线程写入它,只需将一些自动递增的数字附加到基本名称。

    如果您不控制目标,或者需要多线程实现,请使用 GUID。

    【讨论】:

    • 虽然我几乎总是使用 GUID 来解决这个问题,但 LBushkin 在性能方面是正确的。如果您真的想要 每一 滴 perf,递增整数并将其转换为字符串比生成 Guid 并将其转换为字符串要快...但只有在您控制目录。
    【解决方案5】:

    使用 Int 并为每个文件递增。

    【讨论】:

    • OP 想要一个唯一的文件名......那么如果一个名为(例如)"1" 的文件已经存在怎么办?
    【解决方案6】:

    如果您控制目录,则可以根据 lastWriteTime 命名文件:

    DirectoryInfo info = new DirectoryInfo(directoryPath);
    long uniqueKey = info.LastWriteTime.Ticks+1L;
    string filename = String.Format("file{0}.txt", key);
    

    但是您必须检查此代码的性能:我猜构建 DirectoryInfo 不是免费的。

    【讨论】:

    • “建立 DirectoryInfo 不是免费的” - 我怀疑你是对的,特别是如果驱动器/目录不是计算机本地的(网络驱动器/NAS / 等)
    【解决方案7】:

    我希望这个自迭代函数能帮助某人生成一个唯一的文件名。

    public string getUniqueFileName(int i, string fullpath, string filename)
    {
        string lstDir = fullpath.Substring(0, fullpath.LastIndexOf('\\'));
        string name = Path.GetFileName(fullpath);
        string path = fullpath;
    
        if (name != filename)
            path = Path.Combine(lstDir, filename);
    
        if (System.IO.File.Exists(path))
        {
            string ext = Path.GetExtension(name);
            name = Path.GetFileNameWithoutExtension(name);                
            i++;
            filename = getUniqueFileName(i, fullpath, name + "_" + i + ext);
        }
    
        return filename;
    }
    

    【讨论】:

    • 这不会创建file_1_2_3 形式的名称吗?
    • 虽然这会在路径中生成一个唯一的文件名(尽管它不是线程安全的),但与其他仅生成唯一文件名而不检查它是否已经存在的方法相比,它会非常慢。我想操作需要快速生成唯一的文件名。顺便说一句,您的回答解决了问题,并且可能对其他人有所帮助。
    • 是的,确实如此。我的期望是增加相同的数字,但它变成了连接。需要修改代码以使其按预期工作。如果有人更正我的代码,将不胜感激。提前致谢。
    【解决方案8】:

    你可以这样做:

    file.MoveTo(deletedfilesdir + @"\" + f.Name + **DateTime.Now.ToFileTimeUtc()** + f.Extension);
    

    【讨论】:

      猜你喜欢
      • 2010-09-18
      • 1970-01-01
      • 1970-01-01
      • 2013-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多