Как я могу создать временный файл с конкретным расширением .NET? - PullRequest
252 голосов
/ 24 февраля 2009

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

То, что я сейчас делаю, это

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

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

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

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

Ответы [ 16 ]

324 голосов
/ 24 февраля 2009

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

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.

51 голосов
/ 24 февраля 2009

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

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

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

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

41 голосов
/ 14 апреля 2012
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 ();

18 голосов
/ 14 июля 2009

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

8 голосов
/ 01 октября 2012

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

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

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

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

8 голосов
/ 24 февраля 2009

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

string fileName;
do
{
    fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
} while (System.IO.File.Exists(fileName));
6 голосов
/ 27 июля 2010

Как насчет:

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

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

5 голосов
/ 24 февраля 2009

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

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

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

string filename = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv");
3 голосов
/ 06 февраля 2019

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

public static string GetTempFileName(string extension = "csv")
{
    return Path.ChangeExtension(Path.GetTempFileName(), extension);
}
3 голосов
/ 06 января 2015

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

Временное имя файла должно быть

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

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