Как я могу создать временный файл с определенным расширением с помощью .NET?

Мне нужно создать уникальный временный файл с расширением .csv.

Что я делаю прямо сейчас

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

Однако это не гарантирует, что мой файл .csv будет уникальным.

Я знаю, что шансы, что я когда-либо столкнусь, очень низки (особенно если учесть, что я не удаляю файлы .tmp), но этот код мне не нравится.

Конечно, я мог бы вручную сгенерировать случайные имена файлов, пока в конце концов не найду уникальное (что не должно быть проблемой), но мне любопытно узнать, нашли ли другие хороший способ справиться с этой проблемой.

Ответов (17)

Решение

Гарантированная (статистическая) уникальность:

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

(Цитата из вики-статьи о вероятности столкновения:

... годовой риск падения метеорита оценивается в один шанс из 17 миллиардов [19], это означает, что вероятность составляет около 0,00000000006 (6 × 10-11), что эквивалентно шансам создания нескольких десятков триллионы UUID в год и один дубликат. Другими словами, только после генерации 1 миллиарда UUID каждую секунду в течение следующих 100 лет вероятность создания всего одного дубликата составит около 50%. Вероятность одного дубликата составит около 50%, если каждый человек на Земле владеет 600 миллионами UUID.

РЕДАКТИРОВАТЬ: См. Также комментарии JaredPar.

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. Создается пустой файл, чтобы зарезервировать имя файла. Делает 10 попыток в случае коллизий, генерируемых Path.GetRandomFileName ();

Документация MSDN для C++ GetTempFileName обсуждает вашу проблему и дает ответы на нее:

GetTempFileName не может гарантировать уникальность имени файла .

Используются только младшие 16 бит параметра uUnique. Это ограничивает GetTempFileName максимум 65 535 уникальными именами файлов, если параметры lpPathName и lpPrefixString остаются прежними.

Из-за алгоритма, используемого для генерации имен файлов, GetTempFileName может плохо работать при создании большого количества файлов с одним и тем же префиксом. В таких случаях рекомендуется создавать уникальные имена файлов на основе идентификаторов GUID .

Это может быть полезно для вас ... Это для создания временного интервала. папку и верните ее в виде строки в 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

На мой взгляд, большинство ответов, предложенных здесь как неоптимальные. Ближе всего к этому подходит первоначальный вариант, первоначально предложенный Бранном.

Имя временного файла должно быть

  • Уникальный
  • Бесконфликтный (еще не существует)
  • Атомарный (создание имени и файла за одну операцию)
  • Трудно угадать

Из-за этих требований программировать такого зверя самостоятельно - не лучший вариант. Умные люди, пишущие библиотеки ввода-вывода, беспокоятся о таких вещах, как блокировка (при необходимости) и т. Д. Поэтому я не вижу необходимости переписывать 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);

Простая функция в C#:

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

Основываясь на ответах, которые я нашел в Интернете, я пришел к своему коду следующим образом:

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

И как C# Cookbook от Джея Хильярда, Стивен Тейле указал в Использование временного файла в вашем приложении :

  • вы должны использовать временный файл всякий раз, когда вам нужно временно сохранить информацию для последующего поиска.

  • Единственное, что вы должны помнить, - это удалить этот временный файл до того, как приложение, создавшее его, будет закрыто.

  • Если его не удалить, он останется во временном каталоге пользователя, пока пользователь не удалит его вручную.

Это простой, но эффективный способ создания дополнительных имен файлов. Он будет искать непосредственно в текущем (вы можете легко указать это в другом месте) и искать файлы с базовым именем 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;
    }

Попробуйте эту функцию ...

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

Он вернет полный путь с выбранным вами расширением.

Обратите внимание, что создание уникального имени файла не гарантируется, поскольку кто-то другой технически уже мог создать этот файл. Однако вероятность того, что кто-то угадает следующий гид, созданный вашим приложением, и создаст его, очень низка. Можно с уверенностью предположить, что это будет уникально.

Вы также можете сделать следующее

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

и это тоже работает, как ожидалось

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

Почему бы не проверить, существует ли файл?

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

Вы также можете использовать 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.

Как насчет:

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

Маловероятно, что компьютер сгенерирует один и тот же Guid в один и тот же момент времени. Единственная слабость, которую я здесь вижу, - это влияние на производительность DateTime.Now.Ticks.

Вот что я делаю:

строка tStamp = String.Format ("{0: yyyyMMdd.HHmmss}", DateTime.Now);
строка ProcID = Process.GetCurrentProcess (). Id.ToString ();
строка tmpFolder = System.IO.Path.GetTempPath ();
строка outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";

Мне кажется, это нормально работает: он проверяет наличие файла и создает файл, чтобы убедиться, что это доступное для записи место. Если все работает нормально, вы можете изменить его, чтобы он возвращал напрямую 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

Я думаю, вам стоит попробовать это:

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

Он генерирует уникальное имя файла и создает файл с этим именем в указанном месте.

Я смешал ответы @Maxence и @Mitch Wheat, помня, что хочу семантику метода GetTempFileName (имя файла - это имя нового созданного файла), добавив предпочтительное расширение.

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