如何使用.NET创建具有特定扩展名的临时文件?


277

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

我现在要做的是

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

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

我知道发生碰撞的机会非常低(特别是如果您认为我没有删除.tmp文件),但是这段代码对我来说并不好。

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


4
关于GetTempFileName的一些警告如果GetTempFileName方法用于创建超过65535个文件而不删除以前的临时文件,则将引发IOException。如果没有唯一的临时文件名,则GetTempFileName方法将引发IOException。要解决此错误,请删除所有不需要的临时文件。
马克斯·霍奇斯

2
临时文件主要用于特定条件集。如果文件扩展名很重要,我想知道使用GetTempFileName是否不是写解决方案。我知道已经有很长的时间了,但是如果您告诉我们更多有关这些文件的上下文和需求的信息,我们也许可以完全建议一种更好的方法。更多信息,请访问:support.microsoft.com/kb/92635?wa=wsignin1.0
Max Hodges 2012年

1
请记住,每次调用时都会GetTempFileName() 创建一个新文件。-如果立即将字符串更改为其他内容,则只是在temp目录中创建了一个新的零字节文件(并且正如其他人所指出的那样,当您在其中命中65535个文件时,这最终会导致它失败...)- -为避免这种情况,请确保删除您在该文件夹中创建的所有文件(包括GetTempFileName(),最好在finally块中返回的文件)。
BrainSlugs83 '18 -10-23

Answers:


354

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

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的评论。


29
但不能保证将其放置在可写位置
JaredPar'Feb

33
并且它们不能保证完全唯一,从统计学上讲是不可能的。
paxdiablo,

30
@Pax:您连续赢得1000次彩票的机会要多于产生两个理想的指导。我猜那足够独特了……
米奇·

11
@米奇它不是唯一的原因是因为它可以让我简单地创建一个文件在同一路径相同的名称。GUID虽然可以保证是唯一的,但也是可以预测的,这意味着,如果有足够的信息,我就可以猜测您的盒子生成的下一组GUID
JaredPar 2009年

207
好主人们,请尽力将自己的头挡在云端。方法是:生成一个随机文件名,然后创建一个随机文件名(如果不存在)。因此,只需帮助他编写好代码即可。完全没有必要谈论伪随机数生成器和普遍唯一的数字。
马克斯·霍奇斯

58

试试这个功能...

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

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

注意,不能保证产生唯一的文件名,因为其他人从技术上可能已经创建了该文件。但是,有人猜到您的应用产生的下一个GUID并创建它的机会非常低。可以肯定的是,这将是独一无二的。


4
也许Path.ChangeExtension()比Guid.NewGuid()。ToString()+扩展名更优雅
Ohad Schneider 2010年

@ohadsc-确实,Guid.NewGuid().ToString() + extension甚至都不正确,应该是Guid.NewGuid().ToString() + "." + extension
Stephen Swensen

4
我想这取决于方法的约定(无论是期望的.txt还是期望的txt),但是既然能够ChangeExtension处理这两种情况,就不会造成伤害
Ohad Schneider 2012年

1
将其
称为

46
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次尝试。


请记住,Path.GetRandomFileName()实际上是在磁盘上创建一个零字节的文件并返回该文件的完整路径。您不使用此文件,仅使用其名称来更改扩展名。因此,如果您不确定要删除这些临时文件,则在65535调用此函数后,它将开始失败。
弗拉基米尔·巴拉诺夫

7
您混合了GetTempFileName()GetRandomFileName()GetTempFileName()像我的方法一样创建一个零字节文件,但是GetRandomFileName()不创建文件。从文档中:>与GetTempFileName不同,GetRandomFileName不会创建文件。您的链接指向错误的页面。
Maxence

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实例。


10

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

string fileName;
do
{
    fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
} while (System.IO.File.Exists(fileName));

9
File.Exists会告诉您有关过去的信息,因此不可靠。在File.Exist返回和执行代码之间,可以创建文件。
JaredPar

4
...那么您可能对使用自己的程序很安全,但对于其他在那个完全相同的目的地写入文件的过程却
不满意

@JaredPar,这发生的可能性是什么?
Migol

2
@Migol非常低,根据定义,是一种特殊情况。嗯,确切地说是为异常设计的。
科迪·格雷

3
@CodyGray发生Guid冲突的机会是1/2 ^ 128。它将发生2次的机会是1/2 ^ 256等。不要打扰!
Migol 2012年

9

C ++的GetTempFileName的MSDN文档讨论了您的问题并给出了答案:

GetTempFileName无法保证文件名是唯一的

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

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


2
如果将GetTempFileName方法用于创建超过65535个文件而不删除以前的临时文件,则将引发IOException。如果没有唯一的临时文件名,则GetTempFileName方法将引发IOException。要解决此错误,请删除所有不需要的临时文件。
马克斯·霍奇斯

那是不完整的报价。相关引用:“ 如果uUnique不为零,则必须自己创建文件。仅创建文件名,因为GetTempFileName无法保证文件名是唯一的。” 如果您以所有人都在这里讨论的方式来称呼它,那么uUnique将为零。
jnm2 2014年

6

怎么样:

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

计算机极不可能在同一时间生成相同的Guid。我在这里看到的唯一弱点是对DateTime.Now.Ticks的性能影响会增加。


5

您还可以执行以下操作

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

这也按预期工作

string filename = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv");

2
如果存在文件temp.csv,并且您创建temp.tmp,然后将扩展名更改为csv,则此操作将失败
David

不,它不会... GetTempFileName()创建一个唯一的文件名...最大限制为32K,此时您需要删除一些文件,但是我认为我的解决方案是正确的。如果我将不能保证唯一的文件路径传递给ChangeExtension是错误的,但这不是我的解决方案所能做到的。
2009年

11
GetTempFileName保证它返回的路径是唯一的。并不是说它返回的路径+“ .csv”是唯一的。如David所说,以这种方式更改扩展名可能会失败。
Marcus Griep 09年

5
GetTempFileName创建一个文件,因此您的第一个示例是资源泄漏。
加里·麦吉尔

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);

2
但这并不是没有冲突的,“ csv”可能已经存在并被覆盖。
xvan 2016年

3
File.MoveIOException如果目标文件已存在,则引发。msdn.microsoft.com/en-us/library/…–
joshuanapoli

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

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

2

C#中的简易函数:

public static string GetTempFileName(string extension = "csv")
{
    return Path.ChangeExtension(Path.GetTempFileName(), extension);
}

GetTempFileName()在temp文件夹中创建一个临时文件。结果,您将创建两个临时文件,其中一个将泄漏。
evgenybf

1

我混合使用@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;
}

0

这就是我在做什么:

字符串tStamp = String.Format(“ {0:yyyyMMdd.HHmmss}”,DateTime.Now);
字符串ProcID = Process.GetCurrentProcess()。Id.ToString();
字符串tmpFolder = System.IO.Path.GetTempPath();
字符串outFile = tmpFolder + ProcID +“ _” + tStamp +“ .txt”;

良好:包括进程标识符不良:不包括线程标识符(尽管您可以在锁内运行)不良:时间戳仅为1秒的分辨率。在许多应用程序中,通常每秒产生许多文件。
2014年

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;
    }

0

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

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指出了在应用程序使用临时文件

  • 每当需要临时存储信息以便以后检索时,都应该使用一个临时文件。

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

  • 如果未删除,它将保留在用户的临时目录中,直到用户手动将其删除为止。


-2

我认为您应该尝试这样做:

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

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


3
这个解决方案有很多问题。您不能将C:\ temp与绝对路径结合使用,c:\ temp可能不可写,并且不能保证文件的.tmp版本是唯一的。
Mark Lakata 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.