Як я можу створити тимчасовий файл із конкретним розширенням за допомогою .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()ідеалі, в остаточному блоці).
BrainSlugs83

Відповіді:


354

Гарантія бути (статистично) унікальною:

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

(Процитувати зі статті wiki про ймовірність зіткнення:

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

РЕДАКТУЙТЕ: Будь ласка, дивіться також коментарів JaredPar.


29
Але не гарантовано перебуває у записуваному місці
JaredPar

33
І вони НЕ гарантовано бути унікальним на всіх, просто статистично малоймовірно.
paxdiablo

30
@Pax: у вас більше шансів виграти в лотерею 1000 разів поспіль, ніж створити два ідеальних настанови. Це досить унікально, я здогадуюсь ...
Mitch Wheat

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

Він поверне повний шлях із розширенням на ваш вибір.

Зверніть увагу, що не гарантується створення унікального імені файлу, оскільки хтось інший технічно вже створив цей файл. Однак шанси хтось здогадається про наступне керівництво, створене вашим додатком і створить його, дуже низькі. Досить безпечно вважати, що це буде унікально.


4
Можливо, Path.ChangeExtension () був би більш елегантним, ніж Guid.NewGuid (). ToString () + розширення
Охад Шнайдер

@ohadsc - насправді Guid.NewGuid().ToString() + extensionнавіть не правильно, так і повинно бутиGuid.NewGuid().ToString() + "." + extension
Стівен Швенсен

4
Я вважаю, що це залежить від контракту методу (чи очікує він, .txtчи txt), але оскільки він ChangeExtensionвирішує обидва випадки, це не може зашкодити
Охад Шнайдер

1
Назвіть це суфікс замість розширення, і всі раді, я думаю
Chiel ten Brinke

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


Майте на увазі, що Path.GetRandomFileName()насправді створюється нульовий байт на диску та повертає повний шлях до цього файлу. Ви не використовуєте цей файл, лише його ім'я для зміни розширення. Отже, якщо ви не переконайтесь, що ви видаляєте ці тимчасові файли, після 65535 викликів до цієї функції вона почне виходити з ладу.
Володимир Баранов

7
Ви змішали GetTempFileName()і GetRandomFileName(). GetTempFileName()створити файл з нульовим байтом, як мій метод, але GetRandomFileName()не створює файл. З документів:> На відміну від GetTempFileName, GetRandomFileName не створює файл. Ваше посилання вказує на неправильну сторінку.
Максенсія

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, але ви можете вказати все, що завгодно. Я також встановив прапор збереження на 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, і яка поширеність цього відбувається?
Migol

2
@Migol Це дуже низький рівень і, за визначенням, винятковий стан. Гммм, саме те, для чого були створені винятки.
Коді Грей

3
Шанс @CodyGray для зіткнення лідерів становить 1/2 ^ 128. Шанс, що це станеться 2 рази, - це 1/2 ^ 256 і т.д. Не турбуйтеся!
Migol

9

Документація MSDN для GetTempFileName C ++ обговорює вашу проблему та відповідає на неї:

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

Використовуються лише нижні 16 біт параметра unique. Це обмежує GetTempFileName максимум 65,535 унікальних імен файлів, якщо параметри lpPathName та lpPrefixString залишаються однаковими.

Завдяки алгоритму, який використовується для генерування імен файлів, GetTempFileName може погано працювати при створенні великої кількості файлів з тим самим префіксом. У таких випадках рекомендується створювати унікальні імена файлів на основі GUID .


2
Метод GetTempFileName підвищить IOException, якщо він використовується для створення більше 65535 файлів без видалення попередніх тимчасових файлів. Метод GetTempFileName підвищить IOException, якщо не існує унікального тимчасового імені файлу. Щоб усунути цю помилку, видаліть усі непотрібні тимчасові файли.
Макс Ходжес

Це неповна цитата. Відповідна цитата: " Якщо uUnique не дорівнює нулю , ви повинні створити файл самостійно. Створюється лише ім'я файлу, оскільки GetTempFileName не в змозі гарантувати унікальність імені файлу." Якщо ви називаєте це так, як тут всі обговорюють, uUnique буде нульовим.
jnm2

6

Як щодо:

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

Вкрай малоймовірно, що комп'ютер генерує той же Керівництво в той же момент часу. Єдиною слабкістю, яку я бачу тут, є вплив на продуктивність 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, який не гарантовано є унікальним, але це не те, що робить моє рішення.
Майкл Превецький

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 () створює тимчасовий файл у папці 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 ();
string outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";

Добре: включає ідентифікатор процесу Неправильно: не включає ідентифікатор потоку (хоча ви можете запустити це всередині блокування) Погано: мітка часу має роздільну здатність лише 1 секунду. У багатьох, багатьох програмах прийнято створювати багато файлів в секунду.
andrewf

0

Це простий, але ефективний спосіб генерування додаткових імен файлів. Він буде шукати безпосередньо в поточному (ви можете легко вказати, що десь інше) та шукати файли з базою YourApplicationName * .txt (знову ж ви можете легко змінити це). Він розпочнеться з 0000, так що перше ім'я файлу буде YourApplicationName0000.txt. якщо з якоїсь причини є ліва і права частини імен файлів із небажаною (мається на увазі не цифрами), ці файли будуть ігноровані завдяки виклику триспальної передачі.

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

А як Кухарська книга C # Джей Гіляр, Стівен Тейлхет вказав на використання тимчасового файлу у вашій заявці :

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

  • Ви повинні пам’ятати про те, щоб видалити цей тимчасовий файл до того, як програма, яка його створила, була припинена.

  • Якщо його не буде видалено, він залишатиметься у тимчасовому каталозі користувача, поки користувач не видалить його вручну.


-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 версія файлу є унікальною.
Марк Лаката
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.