303

我需要生成一个带有 .csv 扩展名的唯一临时文件。

我现在做的是

string filepath = System.IO.Path.GetTempFileName().Replace(".tmp", ".csv");

但是,这并不能保证我的 .csv 文件是唯一的。

我知道我遇到冲突的机会非常低(特别是如果您认为我不删除 .tmp 文件),但这段代码对我来说并不好。

当然,我可以手动生成随机文件名,直到最终找到一个唯一的文件名(这应该不是问题),但我很想知道其他人是否找到了解决这个问题的好方法。

4

17 回答 17

394

保证是(统计上)唯一的:

string fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv"; 

(引用关于碰撞概率的维基文章:

...一个人每年被陨石击中的风险估计为 170 亿分之一 [19],这意味着概率约为 0.00000000006 (6 × 10−11),相当于产生几十个一年内有数万亿个 UUID,并且有一个重复。换句话说,仅在接下来的 100 年每秒生成 10 亿个 UUID 之后,仅创建一个副本的概率约为 50%。如果地球上每个人都拥有 6 亿个 UUID,那么重复的概率约为 50%

编辑:另请参阅 JaredPar 的评论。

于 2009-02-24T12:29:14.637 回答
62

试试这个功能...

public static string GetTempFilePathWithExtension(string extension) {
  var path = Path.GetTempPath();
  var fileName = Guid.NewGuid().ToString() + extension;
  return Path.Combine(path, fileName);
}

它将返回带有您选择的扩展名的完整路径。

请注意,不能保证生成唯一的文件名,因为从技术上讲,其他人可能已经创建了该文件。然而,有人猜测您的应用程序生成的下一个 guid 并创建它的机会非常低。假设这将是独一无二的,这是非常安全的。

于 2009-02-24T12:38:13.040 回答
53
public static string GetTempFileName(string extension)
{
  int attempt = 0;
  while (true)
  {
    string fileName = Path.GetRandomFileName();
    fileName = Path.ChangeExtension(fileName, extension);
    fileName = Path.Combine(Path.GetTempPath(), fileName);

    try
    {
      using (new FileStream(fileName, FileMode.CreateNew)) { }
      return fileName;
    }
    catch (IOException ex)
    {
      if (++attempt == 10)
        throw new IOException("No unique temporary file name is available.", ex);
    }
  }
}

注意:这类似于 Path.GetTempFileName。创建一个空文件以保留文件名。如果 Path.GetRandomFileName() 产生冲突,它会进行 10 次尝试;

于 2012-04-14T09:20:27.913 回答
19

您也可以使用System.CodeDom.Compiler.TempFileCollection

string tempDirectory = @"c:\\temp";
TempFileCollection coll = new TempFileCollection(tempDirectory, true);
string filename = coll.AddExtension("txt", true);
File.WriteAllText(Path.Combine(tempDirectory,filename),"Hello World");

这里我使用了 txt 扩展名,但你可以指定任何你想要的。我还将keep标志设置为true,以便临时文件在使用后保留。不幸的是,TempFileCollection 会为每个扩展名创建一个随机文件。如果需要更多临时文件,可以创建多个 TempFileCollection 实例。

于 2009-07-14T02:03:10.703 回答
11

为什么不检查文件是否存在?

string fileName;
do
{
    fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
} while (System.IO.File.Exists(fileName));
于 2009-02-24T13:25:06.157 回答
10

C++ 的 GetTempFileName的 MSDN 文档讨论了您的问题并回答了它:

GetTempFileName 不能保证文件名是唯一的

仅使用 uUnique 参数的低 16 位。如果 lpPathName 和 lpPrefixString 参数保持不变,这会将 GetTempFileName 限制为最多 65,535 个唯一文件名。

由于用于生成文件名的算法,GetTempFileName 在创建大量具有相同前缀的文件时性能不佳。在这种情况下,建议您根据 GUID 构造唯一的文件名

于 2012-10-01T15:18:31.347 回答
6

您还可以执行以下操作

string filepath = Path.ChangeExtension(Path.GetTempFileName(), ".csv");

这也可以按预期工作

string filepath = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv");
于 2009-02-24T14:39:08.350 回答
6

怎么样:

Path.Combine(Path.GetTempPath(), DateTime.Now.Ticks.ToString() + "_" + Guid.NewGuid().ToString() + ".csv")

计算机在同一时刻生成相同 Guid 的可能性很小。我在这里看到的唯一弱点是 DateTime.Now.Ticks 将添加的性能影响。

于 2010-07-27T08:12:39.917 回答
3

在我看来,这里提出的大多数答案都是次优的。最接近的一个是最初由 Brann 提出的。

临时文件名必须是

  • 独特
  • 无冲突(尚不存在)
  • 原子(在同一操作中创建名称和文件)
  • 很难猜

由于这些要求,自己编写这样的野兽并不是一个好主意。编写 IO 库的聪明人会担心锁定(如果需要)等问题。因此,我认为没有必要重写 System.IO.Path.GetTempFileName()。

即使它看起来很笨拙,也应该可以完成这项工作:

//Note that this already *creates* the file
string filename1 = System.IO.Path.GetTempFileName()
// Rename and move
filename = filename.Replace(".tmp", ".csv");
File.Move(filename1 , filename);
于 2015-01-06T08:19:43.793 回答
3

我混合了@Maxence@Mitch Wheat答案,记住我想要GetTempFileName 方法的语义(fileName 是创建的新文件的名称)添加首选扩展名。

string GetNewTempFile(string extension)
{
    if (!extension.StartWith(".")) extension="." + extension;
    string fileName;
    bool bCollisions = false;
    do {
        fileName = Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString() + extension);
        try
        {
            using (new FileStream(fileName, FileMode.CreateNew)) { }
            bCollisions = false;
        }
        catch (IOException)
        {
            bCollisions = true;
        }
    }
    while (bCollisions);
    return fileName;
}
于 2019-10-29T14:04:02.967 回答
2

这对你来说可能很方便......这是为了创建一个临时文件。文件夹并将其作为 VB.NET 中的字符串返回。

轻松转换为 C#:

Public Function GetTempDirectory() As String
    Dim mpath As String
    Do
        mpath = System.IO.Path.Combine(System.IO.Path.GetTempPath, System.IO.Path.GetRandomFileName)
    Loop While System.IO.Directory.Exists(mpath) Or System.IO.File.Exists(mpath)
    System.IO.Directory.CreateDirectory(mpath)
    Return mpath
End Function
于 2011-05-30T12:49:30.917 回答
2

这对我来说似乎很好用:它检查文件是否存在并创建文件以确保它是可写位置。应该可以正常工作,您可以将其更改为直接返回 FileStream (这通常是您需要的临时文件):

private string GetTempFile(string fileExtension)
{
  string temp = System.IO.Path.GetTempPath();
  string res = string.Empty;
  while (true) {
    res = string.Format("{0}.{1}", Guid.NewGuid().ToString(), fileExtension);
    res = System.IO.Path.Combine(temp, res);
    if (!System.IO.File.Exists(res)) {
      try {
        System.IO.FileStream s = System.IO.File.Create(res);
        s.Close();
        break;
      }
      catch (Exception) {

      }
    }
  }
  return res;
} // GetTempFile
于 2011-07-25T12:52:43.627 回答
2

C# 中的简单函数:

public static string GetTempFileName(string extension = "csv")
{
    return Path.ChangeExtension(Path.GetTempFileName(), extension);
}
于 2019-02-06T19:19:51.347 回答
1

根据我从互联网上找到的答案,我的代码如下:

public static string GetTemporaryFileName()
{       
    string tempFilePath = Path.Combine(Path.GetTempPath(), "SnapshotTemp");
    Directory.Delete(tempFilePath, true);
    Directory.CreateDirectory(tempFilePath);
    return Path.Combine(tempFilePath, DateTime.Now.ToString("MMddHHmm") + "-" + Guid.NewGuid().ToString() + ".png");
}

正如 Jay Hilyard 的 C# Cookbook 所言,Stephen Teilhet 在Using a Temporary File in Your Application 中指出:

  • 当您需要临时存储信息以供以后检索时,您应该使用临时文件。

  • 您必须记住的一件事是在创建它的应用程序终止之前删除这个临时文件。

  • 如果没有被删除,它将保留在用户的临时目录中,直到用户手动删除它。

于 2019-03-01T08:04:20.887 回答
0

这就是我正在做的事情:

字符串 tStamp = String.Format("{0:yyyyMMdd.HHmmss}", DateTime.Now);
字符串 ProcID = Process.GetCurrentProcess().Id.ToString();
字符串 tmpFolder = System.IO.Path.GetTempPath();
字符串 outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";
于 2011-04-15T20:13:27.357 回答
0

这是生成增量文件名的一种简单但有效的方法。它将直接查看当前(您可以轻松地将其指向其他地方)并搜索具有基本 YourApplicationName*.txt 的文件(同样您可以轻松更改它)。它将从 0000 开始,因此第一个文件名将是 YourApplicationName0000.txt。如果由于某种原因在左右部分(不是数字)之间存在带有垃圾的文件名,则这些文件将通过 tryparse 调用而被忽略。

    public static string CreateNewOutPutFile()
    {
        const string RemoveLeft = "YourApplicationName";
        const string RemoveRight = ".txt";
        const string searchString = RemoveLeft + "*" + RemoveRight;
        const string numberSpecifier = "0000";

        int maxTempNdx = -1;

        string fileName;
        string [] Files = Directory.GetFiles(Directory.GetCurrentDirectory(), searchString);
        foreach( string file in Files)
        {
            fileName = Path.GetFileName(file);
            string stripped = fileName.Remove(fileName.Length - RemoveRight.Length, RemoveRight.Length).Remove(0, RemoveLeft.Length);
            if( int.TryParse(stripped,out int current) )
            {
                if (current > maxTempNdx)
                    maxTempNdx = current;
            }
        }
        maxTempNdx++;
        fileName = RemoveLeft + maxTempNdx.ToString(numberSpecifier) + RemoveRight;
        File.CreateText(fileName); // optional
        return fileName;
    }
于 2018-05-16T20:45:34.570 回答
-2

我认为你应该试试这个:

string path = Path.GetRandomFileName();
path = Path.Combine(@"c:\temp", path);
path = Path.ChangeExtension(path, ".tmp");
File.Create(path);

它生成一个唯一的文件名并在指定位置创建一个具有该文件名的文件。

于 2011-08-22T07:16:55.863 回答