Làm cách nào để tạo tệp tạm thời có phần mở rộng cụ thể với .NET?


277

Tôi cần tạo một tệp tạm thời duy nhất có phần mở rộng .csv.

Những gì tôi làm ngay bây giờ là

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

Tuy nhiên, điều này không đảm bảo rằng tệp .csv của tôi sẽ là duy nhất.

Tôi biết khả năng xảy ra va chạm là rất thấp (đặc biệt nếu bạn cho rằng tôi không xóa các tệp .tmp), nhưng mã này không phù hợp với tôi.

Tất nhiên tôi có thể tự tạo tên tệp ngẫu nhiên cho đến khi cuối cùng tôi tìm thấy một tên duy nhất (không phải là vấn đề), nhưng tôi tò mò muốn biết liệu những người khác có tìm ra cách hay để giải quyết vấn đề này không.


4
Một số lưu ý về GetTempFileName Phương thức GetTempFileName sẽ nâng cao IOException nếu nó được sử dụng để tạo hơn 65535 tệp mà không xóa các tệp tạm thời trước đó. Phương thức GetTempFileName sẽ đưa ra IOException nếu không có tên tệp tạm thời duy nhất. Để khắc phục lỗi này, xóa tất cả các tệp tạm thời không cần thiết.
Max Hodges

2
Các tập tin tạm thời chủ yếu được sử dụng cho một tập hợp các điều kiện cụ thể. Nếu phần mở rộng tệp là quan trọng, tôi tự hỏi liệu có thể sử dụng GetTempFileName không phải là giải pháp ghi. Tôi biết đó là một thời gian dài, nhưng nếu bạn nói với chúng tôi nhiều hơn về bối cảnh và nhu cầu đối với các tệp này, chúng tôi có thể đề xuất một cách tiếp cận tốt hơn hoàn toàn. thêm tại đây: support.microsoft.com/kb/92635?wa=wsignin1.0
Max Hodges

1
Hãy ghi nhớ GetTempFileName() tạo một tệp mới mỗi lần bạn gọi nó. - Nếu bạn ngay lập tức thay đổi chuỗi thành một thứ khác, bạn vừa tạo một tệp byte 0 mới trong thư mục tạm thời của mình (và như những người khác đã lưu ý, điều này cuối cùng sẽ khiến nó bị lỗi khi bạn nhấn 65535 tệp trong đó ...) - - Để tránh điều này, hãy đảm bảo xóa bất kỳ tệp nào bạn tạo trong thư mục đó (bao gồm cả những tệp được trả về GetTempFileName(), lý tưởng là trong một khối cuối cùng).
BrainSlugs83

Câu trả lời:


354

Đảm bảo là duy nhất (theo thống kê):

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

(Để trích dẫn từ bài viết wiki về xác suất xảy ra va chạm:

... Nguy cơ hàng năm của một người bị thiên thạch tấn công được ước tính là một cơ hội trong 17 tỷ [19], điều đó có nghĩa là xác suất vào khoảng 0,000000006 (6 × 10−11), tương đương với tỷ lệ tạo ra vài chục hàng nghìn tỷ UUID trong một năm và có một bản sao. Nói cách khác, chỉ sau khi tạo 1 tỷ UUID mỗi giây trong 100 năm tiếp theo, xác suất tạo ra chỉ một bản sao sẽ là khoảng 50%. Xác suất của một bản sao sẽ là khoảng 50% nếu mỗi người trên trái đất sở hữu 600 triệu UUID

EDIT: Xin vui lòng xem ý kiến ​​của JaredPar.


29
Nhưng không được đảm bảo ở vị trí có thể ghi
JaredPar

33
Và chúng không được đảm bảo là duy nhất, chỉ là không thể thống kê.
paxdiablo

30
@Pax: bạn có nhiều cơ hội trúng xổ số 1000 lần liên tiếp hơn là tạo ra hai hướng dẫn lý tưởng. Tôi đoán là đủ độc đáo ...
Mitch Wheat

11
@Mitch lý do nó không phải là duy nhất là vì tôi có thể chỉ cần tạo một tệp có cùng tên trong cùng một đường dẫn. GUID trong khi được đảm bảo là duy nhất cũng có thể dự đoán được, điều đó có nghĩa là được cung cấp đủ thông tin Tôi có thể đoán bộ hướng dẫn tiếp theo được tạo bởi hộp của bạn
JaredPar

207
người tốt của chúa, cố gắng nhiều hơn để giữ cho đầu của bạn ra khỏi những đám mây. Cách tiếp cận là: tạo một tên tệp ngẫu nhiên, sau đó tạo nó nếu nó không tồn tại. Vì vậy, chỉ cần giúp anh ta mã độc đáo. Tất cả điều này nói về các máy phát giả ngẫu nhiên và các số duy nhất trên toàn cầu là hoàn toàn không cần thiết.
Max Hodges

58

Hãy thử chức năng này ...

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

Nó sẽ trả về một đường dẫn đầy đủ với phần mở rộng của sự lựa chọn của bạn.

Lưu ý, không đảm bảo tạo ra một tên tệp duy nhất vì người khác có thể đã tạo tệp đó về mặt kỹ thuật. Tuy nhiên, khả năng ai đó đoán được hướng dẫn tiếp theo do ứng dụng của bạn tạo ra và tạo ra nó là rất rất thấp. Nó khá an toàn để cho rằng điều này sẽ là duy nhất.


4
Có lẽ Path.ChangeExtension () sẽ thanh lịch hơn Guid.NewGuid (). Phần mở rộng ToString () +
Ohad Schneider

@ohadsc - thực sự, Guid.NewGuid().ToString() + extensionthậm chí không chính xác, nó nên như vậyGuid.NewGuid().ToString() + "." + extension
Stephen Swensen

4
Tôi cho rằng điều đó phụ thuộc vào hợp đồng của phương pháp (dù nó có kỳ vọng .txthay không txt) nhưng vì ChangeExtensionxử lý cả hai trường hợp, nó không thể bị tổn thương
Ohad Schneider

1
Gọi nó là Suffix thay vì Gia hạn và mọi người đều vui mừng, tôi đoán
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);
    }
  }
}

Lưu ý: điều này hoạt động như Path.GetTempFileName. Một tập tin trống được tạo ra để bảo lưu tên tập tin. Nó thực hiện 10 lần thử, trong trường hợp va chạm được tạo bởi Path.GetRandomFileName ();


Hãy nhớ rằng Path.GetRandomFileName()thực sự tạo ra một tệp 0 byte trên đĩa và trả về đường dẫn đầy đủ của tệp đó. Bạn không sử dụng tệp này, chỉ tên của nó để thay đổi phần mở rộng. Vì vậy, nếu bạn không chắc chắn rằng mình đang xóa các tệp tạm thời này, sau 65535 cuộc gọi đến chức năng này, nó sẽ bắt đầu thất bại.
Vladimir Baranov

7
Bạn đã pha trộn GetTempFileName()GetRandomFileName(). GetTempFileName()tạo một tệp không byte như phương thức của tôi, nhưng GetRandomFileName()không tạo tệp. Từ các tài liệu:> Không giống như GetTempFileName, GetRandomFileName không tạo tệp. Liên kết của bạn trỏ đến trang sai.
Tối đa

19

Bạn cũng có thể sử dụng 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");

Ở đây tôi đã sử dụng một phần mở rộng txt nhưng bạn có thể chỉ định bất cứ điều gì bạn muốn. Tôi cũng đặt cờ giữ thành đúng để tệp tạm thời được giữ xung quanh sau khi sử dụng. Thật không may, TempFileCollection tạo một tệp ngẫu nhiên cho mỗi phần mở rộng. Nếu bạn cần thêm tệp tạm thời, bạn có thể tạo nhiều phiên bản TempFileCollection.


10

Tại sao không kiểm tra nếu tập tin tồn tại?

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

9
File.Exists cho bạn biết thông tin về quá khứ và do đó không đáng tin cậy. Ở giữa sự trở lại của File.Exist và mã của bạn đang thực thi, tệp có thể được tạo.
JaredPar

4
... sau đó bạn có thể an toàn với chương trình của riêng mình nhưng không phải với quy trình khác viết một tệp trên cùng đích đó ...
Koen

@JaredPar và khả năng của điều này xảy ra là gì?
Migol

2
@Migol Nó rất thấp và theo định nghĩa là một điều kiện đặc biệt. Hmmm, chính xác những gì ngoại lệ được thiết kế để được sử dụng cho.
Cody Grey

3
@CodyGray cơ hội cho cuộc đụng độ hướng dẫn là 1/2 ^ 128. Rất có thể nó sẽ xảy ra 2 lần là 1/2 ^ 256, v.v. Đừng bận tâm!
Migol

9

Tài liệu MSDN cho GetTempFileName của C ++ thảo luận về mối quan tâm của bạn và trả lời nó:

GetTempFileName không thể đảm bảo rằng tên tệp là duy nhất .

Chỉ có 16 bit thấp hơn của tham số uUnique được sử dụng. Điều này giới hạn GetTempFileName tối đa 65.535 tên tệp duy nhất nếu các tham số lpPathName và lpPrefixString vẫn giữ nguyên.

Do thuật toán được sử dụng để tạo tên tệp, GetTempFileName có thể hoạt động kém khi tạo một số lượng lớn tệp có cùng tiền tố. Trong những trường hợp như vậy, bạn nên tạo tên tệp duy nhất dựa trên GUID .


2
Phương thức GetTempFileName sẽ nâng cao IOException nếu nó được sử dụng để tạo hơn 65535 tệp mà không xóa các tệp tạm thời trước đó. Phương thức GetTempFileName sẽ đưa ra IOException nếu không có tên tệp tạm thời duy nhất. Để khắc phục lỗi này, xóa tất cả các tệp tạm thời không cần thiết.
Tối đa Hodges

Đó là một trích dẫn không đầy đủ. Trích dẫn có liên quan: " Nếu uUnique không bằng 0 , bạn phải tự tạo tệp. Chỉ một tên tệp được tạo, vì GetTempFileName không thể đảm bảo rằng tên tệp là duy nhất." Nếu bạn gọi nó theo cách mọi người đang thảo luận ở đây, uUnique sẽ là con số không.
jnm2

6

Làm thế nào về:

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

Rất có khả năng là máy tính sẽ tạo ra cùng một Hướng dẫn cùng một lúc. Điểm yếu duy nhất tôi thấy ở đây là tác động hiệu suất DateTime.Now.Ticks sẽ thêm.


5

Bạn cũng có thể làm như sau

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

và điều này cũng hoạt động như mong đợi

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

2
điều này sẽ thất bại nếu có tệp temp.csv và bạn tạo temp.tmp và sau đó thay đổi tiện ích mở rộng thành csv
David

Không, nó sẽ không ... GetTempFileName () tạo một tên tệp duy nhất ... tối đa giới hạn 32K tại thời điểm bạn cần xóa một số tệp nhưng tôi nghĩ giải pháp của tôi là chính xác. Thật sai lầm nếu tôi chuyển một đường dẫn tệp vào ChangeExtension không được đảm bảo là duy nhất, nhưng đó không phải là giải pháp của tôi.
Michael Prewecki

11
GetTempFileName đảm bảo rằng đường dẫn mà nó trả về sẽ là duy nhất. Không phải là đường dẫn mà nó trả về + ".csv" sẽ là duy nhất. Thay đổi phần mở rộng theo cách này có thể thất bại như David nói.
Marcus Griep

5
GetTempFileName tạo một tệp, vì vậy ví dụ đầu tiên của bạn là rò rỉ tài nguyên.
Gary McGill

3

Theo tôi, hầu hết các câu trả lời được đề xuất ở đây là tối ưu phụ. Người đến gần nhất là người ban đầu được đề xuất bởi Brann.

Tên tệp tạm thời phải là

  • Độc nhất
  • Không xung đột (chưa tồn tại)
  • Nguyên tử (Tạo tên và tệp trong cùng một hoạt động)
  • Khó đoán

Vì những yêu cầu này, việc tự mình lập trình một con thú như vậy không phải là một ý tưởng thần thánh. Những người thông minh viết thư viện IO lo lắng về những thứ như khóa (nếu cần), v.v. Do đó, tôi thấy không cần phải viết lại System.IO.Path.GetTempFileName ().

Điều này, ngay cả khi nó trông vụng về, nên thực hiện công việc:

//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
Nhưng điều đó không xung đột, 'csv' có thể đã tồn tại và bị ghi đè.
xvan

3
File.Movetăng IOExceptionnếu tập tin đích đã tồn tại. msdn.microsoft.com/en-us/l
Library / từ

2

Điều này có thể hữu ích cho bạn ... Đó là để tạo ra một temp. thư mục và trả về dưới dạng một chuỗi trong VB.NET.

Dễ dàng chuyển đổi sang 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

Điều này có vẻ hoạt động tốt đối với tôi: nó kiểm tra sự tồn tại của tệp và tạo tệp để chắc chắn đó là một vị trí có thể ghi. Nếu hoạt động tốt, bạn có thể thay đổi nó để trả lại trực tiếp FileStream (thường là những gì bạn cần cho tệp tạm thời):

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

Chức năng dễ dàng trong C #:

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

GetTempFileName () tạo một tệp tạm thời trong thư mục tạm thời. Kết quả là bạn sẽ có hai tệp tạm thời được tạo, một trong số đó sẽ bị rò rỉ.
evgenybf

1

Tôi đã trộn lẫn các câu trả lời @Maxence@Mitch Wheat Tôi muốn ngữ nghĩa của phương thức GetTempFileName (fileName là tên của một tệp mới được tạo) thêm phần mở rộng ưa thích.

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

Đây là những gì tôi đang làm:

chuỗi tStamp = String.Format ("{0: yyyyMMdd.HHmmss}", DateTime.Now);
chuỗi ProcID = Process.GetCienProcess (). Id.ToString ();
chuỗi tmpFolder = System.IO.Path.GetTempPath ();
chuỗi outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";

Tốt: bao gồm định danh quy trình Xấu: không bao gồm định danh luồng (mặc dù bạn có thể chạy mã này trong khóa) Xấu: dấu thời gian chỉ có độ phân giải 1 giây. Trong nhiều, nhiều ứng dụng, việc tạo ra nhiều tệp mỗi giây là điều phổ biến.
andrewf

0

Đây là một cách đơn giản nhưng hiệu quả để tạo tên tệp tăng dần. Nó sẽ xem trực tiếp hiện tại (bạn có thể dễ dàng chỉ ra nơi khác) và tìm kiếm các tệp với cơ sở YourApplicationName * .txt (một lần nữa bạn có thể dễ dàng thay đổi điều đó). Nó sẽ bắt đầu lúc 0000 để tên tệp đầu tiên sẽ là YourApplicationName0000.txt. nếu vì lý do nào đó có tên tệp với rác giữa (không phải là số) phần bên trái và bên phải, những tệp đó sẽ bị bỏ qua nhờ vào lệnh gọi thử.

    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

Dựa trên câu trả lời tôi tìm thấy từ internet, tôi tìm đến mã của mình như sau:

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

Và như C # Cookbook của Jay Hilyard, Stephen Teilhet đã chỉ ra việc sử dụng một tệp tạm thời trong ứng dụng của bạn :

  • bạn nên sử dụng một tệp tạm thời bất cứ khi nào bạn cần lưu trữ thông tin tạm thời để phục hồi sau này.

  • Một điều bạn phải nhớ là xóa tệp tạm thời này trước khi ứng dụng đã tạo nó bị chấm dứt.

  • Nếu nó không bị xóa, nó sẽ vẫn còn trong thư mục tạm thời của người dùng cho đến khi người dùng xóa nó theo cách thủ công.


-2

Tôi nghĩ bạn nên thử điều này:

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

Nó tạo một tên tệp duy nhất và tạo một tệp có tên tệp đó tại một vị trí được chỉ định.


3
Giải pháp này có rất nhiều vấn đề với nó. bạn không thể kết hợp C: \ temp với một đường dẫn tuyệt đối, c: \ temp có thể không thể ghi được và không đảm bảo rằng phiên bản .tmp của tệp là duy nhất.
Đánh dấu Lakata
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.