Bagaimana saya bisa membuat file temp dengan ekstensi spesifik dengan .NET?

277

Saya perlu membuat file sementara yang unik dengan ekstensi .csv.

Yang saya lakukan sekarang adalah

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

Namun, ini tidak menjamin bahwa file .csv saya akan unik.

Saya tahu kemungkinan saya pernah mengalami tabrakan sangat rendah (terutama jika Anda menganggap bahwa saya tidak menghapus file .tmp), tetapi kode ini tidak terlihat baik bagi saya.

Tentu saja saya bisa secara manual menghasilkan nama file acak sampai akhirnya saya menemukan yang unik (yang seharusnya tidak menjadi masalah), tetapi saya ingin tahu apakah orang lain telah menemukan cara yang bagus untuk mengatasi masalah ini.

Brann
sumber
4
beberapa peringatan tentang GetTempFileName Metode GetTempFileName akan meningkatkan IOException jika digunakan untuk membuat lebih dari 65535 file tanpa menghapus file sementara sebelumnya. Metode GetTempFileName akan menaikkan IOException jika tidak ada nama file sementara yang unik. Untuk mengatasi kesalahan ini, hapus semua file sementara yang tidak dibutuhkan.
Max Hodges
2
File sementara terutama digunakan untuk sekumpulan kondisi tertentu. Jika ekstensi file penting, saya ingin tahu apakah mungkin menggunakan GetTempFileName bukan solusi menulis. Saya tahu ini sudah lama, tetapi jika Anda memberi tahu kami lebih banyak tentang konteks dan kebutuhan untuk file-file ini, kami mungkin dapat menyarankan pendekatan yang lebih baik sama sekali. lebih lanjut di sini: support.microsoft.com/kb/92635?wa=wsignin1.0
Max Hodges
1
Perlu diingat GetTempFileName() membuat file baru setiap kali Anda menyebutnya. - Jika Anda segera mengubah string ke sesuatu yang lain, Anda baru saja membuat file nol byte baru di direktori temp Anda (dan seperti yang telah dicatat orang lain, ini pada akhirnya akan menyebabkannya gagal ketika Anda menekan 65535 file di sana ...) - - Untuk menghindari ini, pastikan untuk menghapus file yang Anda buat di folder itu (termasuk yang dikembalikan oleh GetTempFileName(), idealnya di blok akhirnya).
BrainSlugs83

Jawaban:

355

Dijamin unik (secara statistik):

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

(Mengutip dari artikel wiki tentang kemungkinan tabrakan:

... risiko tahunan seseorang terkena meteorit diperkirakan satu peluang dalam 17 miliar [19], itu berarti probabilitasnya adalah sekitar 0,00000000006 (6 × 10-10), setara dengan peluang menciptakan beberapa puluh triliunan UUID dalam setahun dan memiliki satu duplikat. Dengan kata lain, hanya setelah menghasilkan 1 miliar UUID setiap detik selama 100 tahun ke depan, kemungkinan membuat hanya satu duplikat adalah sekitar 50%. Probabilitas satu duplikat akan menjadi sekitar 50% jika setiap orang di bumi memiliki 600 juta UUID

EDIT: Silakan juga lihat komentar JaredPar.

Mitch Wheat
sumber
29
Tetapi tidak dijamin berada di lokasi yang dapat ditulis
JaredPar
33
Dan mereka tidak dijamin unik sama sekali, hanya secara statistik tidak mungkin.
paxdiablo
30
@ Max: Anda memiliki chnace lebih banyak untuk memenangkan lotre 1000 kali berturut-turut daripada menghasilkan dua gubernur idebtikal. Kurasa itu cukup unik ...
Mitch Wheat
11
@Mitch alasan itu tidak unik karena itu mungkin bagi saya untuk hanya membuat sebuah file dengan nama yang sama di jalan yang sama. PANDUAN sementara dijamin unik juga dapat diprediksi yang berarti memberikan informasi yang cukup saya bisa menebak set panduan berikutnya yang dihasilkan oleh kotak Anda
JaredPar
207
Tuan-tuan yang baik, cobalah lebih keras untuk menjaga kepala Anda keluar dari awan. Pendekatannya adalah: buat nama file acak, lalu buat jika tidak ada. Jadi bantu dia kode itu dengan baik. Semua pembicaraan tentang generator pseudo-acak dan angka unik universal ini sama sekali tidak perlu.
Max Hodges
58

Coba fungsi ini ...

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

Ini akan mengembalikan jalur penuh dengan ekstensi pilihan Anda.

Catatan, itu tidak dijamin untuk menghasilkan nama file unik karena orang lain secara teknis sudah dapat membuat file itu. Namun kemungkinan seseorang menebak panduan berikutnya yang dihasilkan oleh aplikasi Anda dan membuatnya sangat sangat rendah. Cukup aman untuk menganggap ini akan menjadi unik.

JaredPar
sumber
4
Mungkin Path.ChangeExtension () akan lebih elegan daripada Guid.NewGuid (). ToString () + ekstensi
Ohad Schneider
@ohadsc - memang, Guid.NewGuid().ToString() + extensionbahkan tidak benar, seharusnyaGuid.NewGuid().ToString() + "." + extension
Stephen Swensen
4
Saya kira itu tergantung pada kontrak metode (apakah itu diharapkan .txtatau txt) tetapi karena ChangeExtensionmenangani kedua kasus, tidak ada salahnya
Ohad Schneider
1
Sebut saja Suffix, bukan Extension dan semua orang senang, kurasa
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);
    }
  }
}

Catatan: ini berfungsi seperti Path.GetTempFileName. File kosong dibuat untuk memesan nama file. Itu membuat 10 upaya, dalam kasus tabrakan yang dihasilkan oleh Path.GetRandomFileName ();

Maxence
sumber
Ingatlah bahwa Path.GetRandomFileName()sebenarnya membuat file nol-byte pada disk dan mengembalikan path lengkap file itu. Anda tidak menggunakan file ini, hanya namanya untuk mengubah ekstensi. Jadi, jika Anda tidak memastikan bahwa Anda menghapus file sementara ini, setelah 65535 panggilan ke fungsi ini akan mulai gagal.
Vladimir Baranov
7
Anda telah mencampur GetTempFileName()dan GetRandomFileName(). GetTempFileName()buat file nol-byte seperti metode saya, tetapi GetRandomFileName()tidak membuat file. Dari dokumen:> Tidak seperti GetTempFileName, GetRandomFileName tidak membuat file. Tautan Anda mengarah ke halaman yang salah.
Maxence
19

Anda juga dapat menggunakan 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");

Di sini saya menggunakan ekstensi txt tetapi Anda dapat menentukan apa pun yang Anda inginkan. Saya juga mengatur flag tetap ke true sehingga file temp disimpan setelah digunakan. Sayangnya, TempFileCollection membuat satu file acak per ekstensi. Jika Anda membutuhkan lebih banyak file temp, Anda dapat membuat beberapa instance TempFileCollection.

Mehmet Aras
sumber
10

Mengapa tidak memeriksa apakah file itu ada?

string fileName;
do
{
    fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
} while (System.IO.File.Exists(fileName));
Matías
sumber
9
File.Exists memberi tahu Anda informasi tentang masa lalu dan karenanya tidak dapat diandalkan. Di antara kembalinya File.Exist dan mengeksekusi kode Anda, dimungkinkan untuk membuat file.
JaredPar
4
... maka Anda aman dengan program Anda sendiri mungkin tetapi tidak dengan proses lain menulis file pada tujuan yang sama persis ...
Koen
@JaredPar dan apa kemungkinan terjadi ini?
Migol
2
@ Molig Ini sangat rendah, dan menurut definisi kondisi yang luar biasa. Hmmm, untuk apa pengecualian dirancang untuk digunakan.
Cody Gray
3
@CodyGray kesempatan untuk memandu bentrokan adalah 1/2 ^ 128. Peluang itu akan terjadi 2 kali adalah 1/2 ^ 256 dll. Jangan repot-repot!
Migol
9

Dokumentasi MSDN untuk GetTempFileName C ++ membahas masalah Anda dan menjawabnya:

GetTempFileName tidak dapat menjamin bahwa nama file itu unik .

Hanya 16 bit yang lebih rendah dari parameter uUnique yang digunakan. Ini membatasi GetTempFileName hingga maksimum 65.535 nama file unik jika parameter lpPathName dan lpPrefixString tetap sama.

Karena algoritma yang digunakan untuk menghasilkan nama file, GetTempFileName dapat berkinerja buruk ketika membuat sejumlah besar file dengan awalan yang sama. Dalam kasus seperti itu, Anda disarankan untuk membuat nama file unik berdasarkan GUID .

Kolonel Panic
sumber
2
Metode GetTempFileName akan menaikkan IOException jika digunakan untuk membuat lebih dari 65535 file tanpa menghapus file sementara sebelumnya. Metode GetTempFileName akan menaikkan IOException jika tidak ada nama file sementara yang unik. Untuk mengatasi kesalahan ini, hapus semua file sementara yang tidak dibutuhkan.
Max Hodges
Itu adalah kutipan yang tidak lengkap. Kutipan yang relevan: " Jika uUnique bukan nol , Anda harus membuat file sendiri. Hanya nama file yang dibuat, karena GetTempFileName tidak dapat menjamin bahwa nama file itu unik." Jika Anda menyebutnya seperti yang dibahas semua orang di sini, uUnique akan menjadi nol.
jnm2
6

Bagaimana tentang:

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

Sangat tidak mungkin bahwa komputer akan menghasilkan Guid yang sama pada saat yang bersamaan. Satu-satunya kelemahan yang saya lihat di sini adalah dampak kinerja DateTime.Now.Ticks akan menambahkan.

IRBaboon
sumber
5

Anda juga dapat melakukan hal berikut

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

dan ini juga berfungsi seperti yang diharapkan

string filename = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv");
Michael Prewecki
sumber
2
ini akan gagal jika ada file temp.csv dan Anda membuat temp.tmp dan kemudian mengubah ekstensi ke csv
David
Tidak itu tidak akan ... GetTempFileName () membuat nama file yang unik ... hingga batas 32K pada titik mana Anda perlu menghapus beberapa file tapi saya pikir solusi saya sudah benar. Salah jika saya meneruskan jalur file ke ChangeExtension yang tidak dijamin unik, tapi bukan itu yang dilakukan solusi saya.
Michael Prewecki
11
GetTempFileName menjamin bahwa jalur yang dikembalikannya akan unik. Bukan berarti jalur yang dikembalikan + ".csv" akan menjadi unik. Mengubah ekstensi dengan cara ini bisa gagal seperti kata David.
Marcus Griep
5
GetTempFileName membuat file, jadi contoh pertama Anda adalah kebocoran sumber daya.
Gary McGill
3

Menurut pendapat saya, sebagian besar jawaban yang diajukan di sini sebagai kurang optimal. Yang paling dekat adalah yang asli yang diusulkan pada awalnya oleh Brann.

Nama file Temp harus

  • Unik
  • Bebas konflik (belum ada)
  • Atomic (Pembuatan Nama & File dalam operasi yang sama)
  • Sulit ditebak

Karena persyaratan ini, itu bukan ide yang bagus untuk memprogram binatang buas seperti itu sendiri. Orang-orang pintar yang menulis Perpustakaan IO khawatir tentang hal-hal seperti penguncian (jika perlu) dll. Oleh karena itu, saya melihat tidak perlu menulis ulang System.IO.Path.GetTempFileName ().

Ini, meskipun terlihat canggung, harus melakukan pekerjaan:

//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);
Daniel C. Oderbolz
sumber
2
Tapi itu bukan bebas konflik, 'csv' sudah bisa ada dan ditimpa.
xvan
3
File.Movememunculkan IOExceptionjika file tujuan sudah ada. msdn.microsoft.com/en-us/library/…
joshuanapoli
2

Ini bisa berguna untuk Anda ... Ini untuk membuat temp. folder dan mengembalikannya sebagai string di VB.NET.

Mudah dikonversi ke 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
Simon
sumber
2

Ini sepertinya berfungsi dengan baik bagi saya: memeriksa keberadaan file dan membuat file untuk memastikan itu adalah lokasi yang dapat ditulis. Seharusnya berfungsi dengan baik, Anda dapat mengubahnya untuk mengembalikan langsung FileStream (yang biasanya Anda butuhkan untuk file temp):

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
Paolo Iommarini
sumber
2

Fungsi mudah di C #:

public static string GetTempFileName(string extension = "csv")
{
    return Path.ChangeExtension(Path.GetTempFileName(), extension);
}
Peternakan Camal
sumber
GetTempFileName () membuat file sementara di folder temp. Akibatnya, Anda akan memiliki dua file sementara yang dibuat, salah satunya akan bocor.
evgenybf
1

Berdasarkan jawaban yang saya temukan dari internet, saya membuka kode saya sebagai berikut:

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

Dan seperti C # Cookbook oleh Jay Hilyard, Stephen Teilhet menunjuk dalam Menggunakan File Sementara di Aplikasi Anda :

  • Anda harus menggunakan file sementara kapan pun Anda perlu menyimpan informasi sementara untuk pengambilan nanti.

  • Satu hal yang harus Anda ingat adalah menghapus file sementara ini sebelum aplikasi yang membuatnya dihentikan.

  • Jika tidak dihapus, itu akan tetap berada di direktori sementara pengguna sampai pengguna menghapusnya secara manual.

Speedoops
sumber
1

Saya mencampur @Maxence dan @Mitch Wheat menjawab dengan mengingat bahwa saya ingin semantik metode GetTempFileName (fileName adalah nama file baru yang dibuat) menambahkan ekstensi yang disukai.

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
sumber
0

Inilah yang saya lakukan:

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";
Michael Fitzpatrick
sumber
Bagus: termasuk pengidentifikasi proses Buruk: tidak termasuk pengenal utas (meskipun Anda dapat menjalankan ini di dalam kunci) Buruk: timestamp hanya memiliki resolusi 1 detik. Dalam banyak, banyak aplikasi, adalah umum untuk menghasilkan banyak file per detik.
andrewf
0

Ini adalah cara sederhana namun efektif untuk menghasilkan nama file tambahan. Ini akan melihat langsung saat ini (Anda dapat dengan mudah mengarahkannya ke tempat lain) dan mencari file dengan basis YourApplicationName * .txt (sekali lagi Anda dapat dengan mudah mengubahnya). Ini akan dimulai pada 0000 sehingga nama file pertama adalah YourApplicationName0000.txt. jika karena alasan tertentu ada nama file dengan junk antara (artinya bukan angka) bagian kiri dan kanan, file-file itu akan diabaikan berdasarkan panggilan 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;
    }
Nama tampilan
sumber
-2

Saya pikir Anda harus mencoba ini:

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

Ini menghasilkan nama file unik dan membuat file dengan nama file itu di lokasi yang ditentukan.

Smita
sumber
3
Solusi ini memiliki begitu banyak masalah dengannya. Anda tidak dapat menggabungkan C: \ temp dengan path absolut, c: \ temp mungkin tidak dapat ditulisi, dan itu tidak menjamin bahwa versi .tmp dari file tersebut unik.
Mark Lakata