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

277

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

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

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

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

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

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

Бранн
источник
4
некоторые предостережения относительно GetTempFileName Метод GetTempFileName вызовет IOException, если он используется для создания более 65535 файлов без удаления предыдущих временных файлов. Метод GetTempFileName вызовет IOException, если уникальное временное имя файла недоступно. Чтобы устранить эту ошибку, удалите все ненужные временные файлы.
Макс Ходжес
2
Временные файлы в основном используются для определенного набора условий. Если расширение файла важно, мне интересно, может быть, использование GetTempFileName не является решением для записи. Я знаю, что это было давно, но если бы вы рассказали нам больше о контексте и необходимости этих файлов, мы могли бы предложить лучший подход в целом. больше здесь: support.microsoft.com/kb/92635?wa=wsignin1.0
Макс Ходжес
1
Имейте в виду, GetTempFileName() создает новый файл каждый раз, когда вы вызываете его. - Если вы немедленно измените строку на что-то другое, вы просто создали новый нулевой байт-файл во временном каталоге (и, как уже отмечали другие, это в конечном итоге приведет к сбою при попадании туда 65535 файлов ...) - - Чтобы избежать этого, обязательно удалите все файлы, которые вы создаете в этой папке (включая те, которые были возвращены GetTempFileName(), в идеале, в блоке finally).
BrainSlugs83

Ответы:

356

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

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.

Митч Пшеничный
источник
29
Но не гарантированно находиться в доступном для записи месте
JaredPar
33
И они вовсе не гарантированы быть уникальными, просто статистически маловероятно.
paxdiablo
30
@Pax: у вас больше шансов выиграть в лотерею 1000 раз подряд, чем создать два идеальных гида. Это достаточно уникально, я думаю ...
Митч Уит
11
@Mitch причина , это не является уникальным, потому что это возможно для меня , чтобы просто создать файл с таким же именем в том же пути. Хотя идентификаторы GUID гарантированно являются уникальными, они также предсказуемы, что означает, что при наличии достаточного количества информации я могу предположить следующий набор направляющих, сгенерированный вашим устройством
JaredPar
207
добрые люди, старайтесь изо всех сил держать голову подальше от облаков. Подход заключается в следующем: создать случайное имя файла, а затем создать его, если оно не существует. Так что просто помогите ему это красиво написать. Все эти разговоры о псевдослучайных генераторах и универсально уникальных числах совершенно не нужны.
Макс Ходжес
58

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

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

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

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

JaredPar
источник
4
Возможно, Path.ChangeExtension () будет более элегантным, чем Guid.NewGuid (). ToString () + расширение
Охад Шнайдер
@ ohadsc - действительно, Guid.NewGuid().ToString() + extensionдаже не правильно, так и должно бытьGuid.NewGuid().ToString() + "." + extension
Стивен Свенсен
4
Я предполагаю, что это зависит от контракта метода (ожидает ли он .txtили txt), но так ChangeExtensionкак обрабатывает оба случая, это не может повредить
Охад Шнайдер
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. Пустой файл создается, чтобы зарезервировать имя файла. Это делает 10 попыток, в случае коллизий, сгенерированных Path.GetRandomFileName ();

Maxence
источник
Имейте в виду, что 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
... тогда вы можете быть в безопасности со своей собственной программой, может быть, но не с другим процессом, пишущим файл в том же месте назначения ...
Koen
@JaredPar, и какова вероятность этого?
Мигол,
2
@Migol Это очень низко, и по определению исключительное условие. Хм, именно то, для чего были разработаны исключения.
Коди Грей
3
@CodyGray шанс для столкновения guid составляет 1/2 ^ 128. Вероятность того, что это произойдет 2 раза, составляет 1/2 ^ 256 и т. Д. Не беспокойтесь!
Migol
10

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

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

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

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

Полковник паника
источник
2
Метод GetTempFileName вызовет IOException, если он используется для создания более 65535 файлов без удаления предыдущих временных файлов. Метод GetTempFileName вызовет IOException, если уникальное временное имя файла недоступно. Чтобы устранить эту ошибку, удалите все ненужные временные файлы.
Макс Ходжес
Это неполная цитата. Соответствующая цитата: « Если uUnique не равен нулю , вы должны создать файл самостоятельно. Создается только имя файла, потому что GetTempFileName не может гарантировать, что имя файла уникально». Если вы называете это так, как все здесь обсуждают, то UnUnique будет равно нулю.
jnm2
6

Как насчет:

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

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

IRBaboon
источник
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, который не гарантированно будет уникальным, но это не то, что делает мое решение.
Майкл Превецки
11
GetTempFileName гарантирует, что возвращаемый путь будет уникальным. Не то чтобы путь, который он возвращает + ".csv" будет уникальным. Изменение расширения таким образом может потерпеть неудачу, как сказал Дэвид.
Маркус Грип
5
GetTempFileName создает файл, поэтому ваш первый пример - утечка ресурсов.
Гэри МакГилл
3

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

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

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

Из-за этих требований не стоит программировать такого зверя самостоятельно. Умные люди, пишущие библиотеки ввода-вывода, беспокоятся о таких вещах, как блокировка (при необходимости) и т. Д. Поэтому я не вижу необходимости переписывать 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
3
File.MoveВозникает, IOExceptionесли целевой файл уже существует. 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 () создает временный файл во временной папке. В результате у вас будет создано два временных файла, один из которых будет иметь утечку.
evgenybf
@evgenybf, мы не создаем два файла. Мы генерируем файл, затем меняем расширение.
Ранчо Камаль
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");
}

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

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

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

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

Speedoops
источник
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;
}
Fil
источник
0

Это то, что я делаю:

string tStamp = String.Format ("{0: yyyyMMdd.HHmmss}", DateTime.Now);
string ProcID = Process.GetCurrentProcess (). Id.ToString ();
string tmpFolder = System.IO.Path.GetTempPath ();
string outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";
Майкл Фитцпатрик
источник
Хорошо: включает в себя идентификатор процесса. Плохо: не включает в себя идентификатор потока (хотя вы можете запустить его внутри блокировки). Плохо: временная метка имеет разрешение всего 1 секунду. Во многих, многих приложениях обычно создается много файлов в секунду.
Андрей
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;
    }
Отображаемое имя
источник
-2

Я думаю, вы должны попробовать это:

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

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

Smita
источник
3
Это решение имеет так много проблем с ним. Вы не можете комбинировать C: \ temp с абсолютным путем, c: \ temp может быть недоступен для записи, и это не гарантирует, что версия файла .tmp является уникальной.
Марк Лаката