.NETで特定の拡張子を持つ一時ファイルを作成するにはどうすればよいですか?


277

拡張子が.csvの一意の一時ファイルを生成する必要があります。

私が今やっていることは

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

ただし、これは.csvファイルが一意であることを保証するものではありません。

衝突が発生する可能性は非常に低いことはわかっていますが(特に、.tmpファイルを削除しないと考える場合)、このコードは私にはよく見えません。

もちろん、ランダムなファイル名を手動で生成して、最終的に一意のファイル名を見つけることもできます(問題にはなりません)が、他の人がこの問題に対処する良い方法を見つけたかどうか知りたいです。


4
GetTempFileNameに関するいくつかの警告GetTempFileNameメソッドは、以前の一時ファイルを削除せずに65535を超えるファイルを作成するために使用される場合、IOExceptionを発生させます。GetTempFileNameメソッドは、使用可能な一意の一時ファイル名がない場合、IOExceptionを発生させます。このエラーを解決するには、不要な一時ファイルをすべて削除します。
Max Hodges

2
一時ファイルは、主に特定の条件セットで使用されます。ファイル拡張子が重要な場合、GetTempFileNameを使用することが書き込みソリューションではないのではないかと思います。久しぶりのことですが、これらのファイルのコンテキストと必要性について詳しく教えていただければ、より適切なアプローチを提案できるかもしれません。詳細:support.microsoft.com/kb/92635
Max Hodges

1
呼び出すたびに新しいファイルGetTempFileName() が作成されることに注意してください。-文字列をすぐに別の文字列に変更すると、一時ディレクトリに新しいゼロバイトファイルが作成されます(他の人が指摘しているように、そこに65535個のファイルがあると、最終的に失敗します...)- -これを回避するには、そのフォルダーに作成したファイル(によって返されるファイルを含むGetTempFileName()、理想的には、finallyブロック)を必ず削除してください。
BrainSlugs83

回答:


354

(統計的に)一意であることが保証されています。

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

(衝突の確率に関するウィキの記事から引用するには:

...隕石に当たる1年あたりのリスクは、170億回に1回と推定されます[19]。これは、確率が約0.00000000006(6×10-11)であることを意味し、数十の確率で作成されます1年間で数兆のUUIDがあり、1つの重複があります。つまり、次の100年間、毎秒10億個のUUIDを生成した後にのみ、複製が1つだけ作成される確率は約50%になります。地球上のすべての人が6億のUUIDを所有している場合、1つの重複の確率は約50%になります

編集:JaredParのコメントも参照してください。


29
ただし、書き込み可能な場所にあることが保証されていない
JaredPar

33
そして、それらが一意であることはまったく保証されていません。
paxdiablo 2009

30
@Pax:2つのアイデアGUIDを生成するよりも、連続で1000回宝くじに勝つチャンスが多くあります。それは十分にユニークだと思います...
ミッチウィート

11
@Mitchが一意でない理由は、同じパスに同じ名前のファイルを単に作成することが可能だからです。一意であることが保証されているGUIDも予測可能です。つまり、十分な情報があれば、ボックスによって生成される次のGUIDセットを推測できます
JaredPar

207
良い主人よ、頭を雲に近づけないようにもっと頑張ってください。アプローチは次のとおりです。ランダムなファイル名を生成し、それが存在しない場合は作成します。だから、彼がうまくコードするのを手伝ってください。疑似乱数発生器と普遍的にユニークな数についてのこのすべての話は完全に不要です。
Max Hodges

58

この機能を試してみてください...

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

選択した拡張子を含むフルパスを返します。

誰かが技術的にそのファイルをすでに作成している可能性があるため、一意のファイル名が生成されるとは限りません。ただし、アプリが生成した次のGUIDを誰かが推測して作成する可能性は非常に低いです。これがユニークであると仮定することはかなり安全です。


4
おそらく、Path.ChangeExtension()は、Guid.NewGuid()。ToString()+拡張よりもエレガントになるでしょう
Ohad Schneider

@ohadsc-確かにGuid.NewGuid().ToString() + extension、それも正しくありません。それは正しいはずですGuid.NewGuid().ToString() + "." + extension
Stephen

4
私は、メソッドの契約に依存していると仮定し(それは期待するかどう.txttxt)が、以来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のように機能します。空のファイルが作成され、ファイル名が予約されます。Path.GetRandomFileName()によって生成された衝突の場合、10回試行します。


Path.GetRandomFileName()実際にディスク上にゼロバイトのファイルを作成し、そのファイルの完全パスを返すことに注意してください。このファイルは使用せず、拡張子を変更するための名前のみを使用します。したがって、これらの一時ファイルを削除していることを確認しないと、65535がこの関数を呼び出した後に失敗します。
Vladimir Baranov 2017

7
あなたは混合GetTempFileName()していGetRandomFileName()ます。GetTempFileName()私の方法のようにゼロバイトのファイルをGetRandomFileName()作成しますが、ファイルは作成しません。ドキュメントから:> GetTempFileNameとは異なり、GetRandomFileNameはファイルを作成しません。リンクが間違ったページを指しています。
Maxence 2017

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は拡張子ごとに1つのランダムファイルを作成します。さらに一時ファイルが必要な場合は、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などです。
ミゴール

9

C ++のGetTempFileNameに関するMSDNドキュメントで、懸念事項について説明し、それに回答します。

GetTempFileNameは、ファイル名が一意であることを保証できません

uUniqueパラメータの下位16ビットのみが使用されます。これにより、lpPathNameパラメータとlpPrefixStringパラメータが同じ場合、GetTempFileNameは最大65,535個の一意のファイル名に制限されます。

ファイル名の生成に使用されるアルゴリズムが原因で、同じ接頭辞を持つ多数のファイルを作成すると、GetTempFileNameのパフォーマンスが低下する場合があります。このような場合は、GUIDに基づいて一意のファイル名を作成することをお勧めします


2
GetTempFileNameメソッドを使用して、以前の一時ファイルを削除せずに65535を超えるファイルを作成すると、IOExceptionが発生します。GetTempFileNameメソッドは、使用可能な一意の一時ファイル名がない場合、IOExceptionを発生させます。このエラーを解決するには、不要な一時ファイルをすべて削除してください。
Max Hodges

それは不完全な引用です。関連する引用:「uUniqueがゼロない場合は、ファイルを自分で作成する必要があります。GetTempFileNameはファイル名が一意であることを保証できないため、ファイル名のみが作成されます。」みんながここで話し合っている方法で呼ぶと、uUniqueはゼロになります。
jnm2 2014

6

どうですか:

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

コンピューターが同じ瞬間に同じGuidを生成することはほとんどありません。ここで私が目にする唯一の弱点は、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に渡したのは誤りですが、それは私のソリューションでは行いません。
Michael Prewecki、2009年

11
GetTempFileNameは、返されるパスが一意であることを保証します。+ ".csv"が返すパスが一意になるわけではありません。このように拡張子を変更すると、Davidが言ったように失敗する可能性があります。
Marcus Griep、

5
GetTempFileNameはファイルを作成するため、最初の例はリソースリークです。
ゲイリーマギル

3

私の意見では、ほとんどの回答はここでは最適とは言えません。最も近いものは、最初にBrannによって提案されたオリジナルのものです。

一時ファイル名は

  • ユニークな
  • 競合なし(まだ存在していない)
  • アトミック(同じ操作での名前とファイルの作成)
  • 推測しにくい

これらの要件のために、そのような獣を自分でプログラムするのはおかしな考えではありません。IOライブラリを作成するスマートピープルは、ロック(必要な場合)などについて心配しています。したがって、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()は、一時フォルダーに一時ファイルを作成します。その結果、2つの一時ファイルが作成され、そのうちの1つがリークします。
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

これは私がやっていることです:

string tStamp = String.Format( "{0:yyyyMMdd.HHmmss}"、DateTime.Now);
文字列ProcID = Process.GetCurrentProcess()。Id.ToString();
文字列tmpFolder = System.IO.Path.GetTempPath();
文字列outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";

良い:プロセス識別子を含む悪い:スレッド識別子を含まない(これをロック内で実行することはできます)悪い:タイムスタンプは1秒の解像度にすぎません。多くの多くのアプリケーションでは、毎秒多くのファイルを生成するのが一般的です。
andrewf 2014年

0

これは、増分ファイル名を生成するためのシンプルですが効果的な方法です。これは、現在のファイルを直接調べ(簡単に別の場所を指すことができます)、ベースがYourApplicationName * .txtのファイルを検索します(これも簡単に変更できます)。最初のファイル名がYourApplicationName0000.txtになるように、0000から始まります。何らかの理由で、左側と右側の部分の間に(数字ではなく)ジャンクのあるファイル名がある場合、それらのファイルは、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;
    }

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

そして、Jay HilyardによるC#クックブックとして、Stephen Teilhetがアプリケーションでの一時ファイルの使用を指摘しました。

  • 後で取得するために情報を一時的に格納する必要がある場合は、一時ファイルを使用する必要があります。

  • 覚えておく必要があるのは、この一時ファイルを、それを作成したアプリケーションが終了する前に削除することです。

  • 削除されない場合、ユーザーが手動で削除するまで、ユーザーの一時ディレクトリに残ります。


-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バージョンが一意であることを保証しません。
Mark Lakata、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.