C#:数字を追加して一意のファイル名を作成するにはどうすればよいでしょうか?
質問
ファイル名をいずれかとして受け取るメソッドを作成したいと考えています。 string
または FileInfo
ファイルが存在する場合は、ファイル名にインクリメントされた番号が追加されます。しかし、これを良い方法で行う方法がまったくわかりません。
たとえば、この FileInfo がある場合
var file = new FileInfo(@"C:\file.ext");
新しい FileInfo を提供するメソッドが欲しいです。 C:\ファイル1.ext もし C:\ファイル.ext存在し、そして C:\ファイル2.ext もし C:\ファイル1.ext などがありました。このようなもの:
public FileInfo MakeUnique(FileInfo fileInfo)
{
if(fileInfo == null)
throw new ArgumentNullException("fileInfo");
if(!fileInfo.Exists)
return fileInfo;
// Somehow construct new filename from the one we have, test it,
// then do it again if necessary.
}
解決 2
ここで良いアドバイスがたくさん。私は<のhref = "https://stackoverflow.com/questionsにマークするによって書かれた方法を使用して終了しました/ 909521 /ハウツー-解決-この-問題格納値-persistenly・オブ・ファイル・イン・ディレクトリ/ 909545#909545" >別の質問のへの答え。それをほんの少しを再フォーマットし、「外部から」を使用することを少し簡単にするために、別の方法を追加しました。ここでの結果は次のとおりです。
private static string numberPattern = " ({0})";
public static string NextAvailableFilename(string path)
{
// Short-cut if already available
if (!File.Exists(path))
return path;
// If path has extension then insert the number pattern just before the extension and return next filename
if (Path.HasExtension(path))
return GetNextFilename(path.Insert(path.LastIndexOf(Path.GetExtension(path)), numberPattern));
// Otherwise just append the pattern to the path and return next filename
return GetNextFilename(path + numberPattern);
}
private static string GetNextFilename(string pattern)
{
string tmp = string.Format(pattern, 1);
if (tmp == pattern)
throw new ArgumentException("The pattern must include an index place-holder", "pattern");
if (!File.Exists(tmp))
return tmp; // short-circuit if no matches
int min = 1, max = 2; // min is inclusive, max is exclusive/untested
while (File.Exists(string.Format(pattern, max)))
{
min = max;
max *= 2;
}
while (max != min + 1)
{
int pivot = (max + min) / 2;
if (File.Exists(string.Format(pattern, pivot)))
min = pivot;
else
max = pivot;
}
return string.Format(pattern, max);
}
部分的にしか今のところ、それをテストしたが、私はそれでバグを見つけた場合に更新されます。 (マークのSコードがうまく動作!)あなたはそれで何か問題を見つけた場合は、コメントや編集か何かしてください:)
他のヒント
public FileInfo MakeUnique(string path)
{
string dir = Path.GetDirectoryName(path);
string fileName = Path.GetFileNameWithoutExtension(path);
string fileExt = Path.GetExtension(path);
for (int i = 1; ;++i) {
if (!File.Exists(path))
return new FileInfo(path);
path = Path.Combine(dir, fileName + " " + i + fileExt);
}
}
もちろん、これは他の回答で述べたように競合状態に脆弱性があります。
あまりきれいではありませんが、私はしばらくこれを持っていました:
private string getNextFileName(string fileName)
{
string extension = Path.GetExtension(fileName);
int i = 0;
while (File.Exists(fileName))
{
if (i == 0)
fileName = fileName.Replace(extension, "(" + ++i + ")" + extension);
else
fileName = fileName.Replace("(" + i + ")" + extension, "(" + ++i + ")" + extension);
}
return fileName;
}
ファイルがすでに存在すると仮定します。
- ファイル.txt
- ファイル(1).txt
- ファイル(2).txt
getNextFileName("File.txt") を呼び出すと、"File(3).txt" が返されます。
バイナリ検索を使用しないため、最も効率的ではありませんが、ファイル数が少ない場合には問題ありません。そして、競合状態は考慮されていません...
ファイルが存在するかどうかをチェックすることはあまりにもハードあなたはいつもそれを一意にするために、ファイル名に日付と時刻を追加することができている場合:
FileName.YYYYMMDD.HHMMSS
必要に応じて、多分ミリ秒を追加します。
フォーマットは、あなたを気にしない場合は、呼び出すことができます:
try{
string tempFile=System.IO.Path.GetTempFileName();
string file=System.IO.Path.GetFileName(tempFile);
//use file
System.IO.File.Delete(tempFile);
}catch(IOException ioe){
//handle
}catch(FileIOPermission fp){
//handle
}
PS: - でこれについての詳細をお読みくださいMSDNには、使用する前にします。
/// <summary>
/// Create a unique filename for the given filename
/// </summary>
/// <param name="filename">A full filename, e.g., C:\temp\myfile.tmp</param>
/// <returns>A filename like C:\temp\myfile633822247336197902.tmp</returns>
public string GetUniqueFilename(string filename)
{
string basename = Path.Combine(Path.GetDirectoryName(filename),
Path.GetFileNameWithoutExtension(filename));
string uniquefilename = string.Format("{0}{1}{2}",
basename,
DateTime.Now.Ticks,
Path.GetExtension(filename));
// Thread.Sleep(1); // To really prevent collisions, but usually not needed
return uniquefilename;
}
DateTime.Ticksとして100ナノ秒の分解能を有します、衝突は極めてまれです。しかし、のThread.sleep(1)のことを確認しますが、私はそれが必要だということを疑う。
ファイル名に新しいGUIDを挿入します。
アイデアは、既存のファイルのリストを取得数字を解析し、次に高いものを作ることです。
注:これは、あなたがこれらのファイルを作成する一つのスレッドよりも多くを持っているので、もし、がのように注意して、競合状態に脆弱である。
。注2:これは未テストです。
public static FileInfo GetNextUniqueFile(string path)
{
//if the given file doesn't exist, we're done
if(!File.Exists(path))
return new FileInfo(path);
//split the path into parts
string dirName = Path.GetDirectoryName(path);
string fileName = Path.GetFileNameWithoutExtension(path);
string fileExt = Path.GetExtension(path);
//get the directory
DirectoryInfo dir = new DirectoryInfo(dir);
//get the list of existing files for this name and extension
var existingFiles = dir.GetFiles(Path.ChangeExtension(fileName + " *", fileExt);
//get the number strings from the existing files
var NumberStrings = from file in existingFiles
select Path.GetFileNameWithoutExtension(file.Name)
.Remove(0, fileName.Length /*we remove the space too*/);
//find the highest existing number
int highestNumber = 0;
foreach(var numberString in NumberStrings)
{
int tempNum;
if(Int32.TryParse(numberString, out tempnum) && tempNum > highestNumber)
highestNumber = tempNum;
}
//make the new FileInfo object
string newFileName = fileName + " " + (highestNumber + 1).ToString();
newFileName = Path.ChangeExtension(fileName, fileExt);
return new FileInfo(Path.Combine(dirName, newFileName));
}
、あなたはすでに存在していると、あなたのアルゴリズムに従って第1のギャップを見つけるファイルのリストを求めることができます。
public static class FileInfoExtensions
{
public static FileInfo MakeUnique(this FileInfo fileInfo)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
string newfileName = new FileUtilities().GetNextFileName(fileInfo.FullName);
return new FileInfo(newfileName);
}
}
public class FileUtilities
{
public string GetNextFileName(string fullFileName)
{
if (fullFileName == null)
{
throw new ArgumentNullException("fullFileName");
}
if (!File.Exists(fullFileName))
{
return fullFileName;
}
string baseFileName = Path.GetFileNameWithoutExtension(fullFileName);
string ext = Path.GetExtension(fullFileName);
string filePath = Path.GetDirectoryName(fullFileName);
var numbersUsed = Directory.GetFiles(filePath, baseFileName + "*" + ext)
.Select(x => Path.GetFileNameWithoutExtension(x).Substring(baseFileName.Length))
.Select(x =>
{
int result;
return Int32.TryParse(x, out result) ? result : 0;
})
.Distinct()
.OrderBy(x => x)
.ToList();
var firstGap = numbersUsed
.Select((x, i) => new { Index = i, Item = x })
.FirstOrDefault(x => x.Index != x.Item);
int numberToUse = firstGap != null ? firstGap.Item : numbersUsed.Count;
return Path.Combine(filePath, baseFileName) + numberToUse + ext;
}
}
ここでは、ファイルシステムのチェックから番号の命名質問を切り離し一つです
/// <summary>
/// Finds the next unused unique (numbered) filename.
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <param name="inUse">Function that will determine if the name is already in use</param>
/// <returns>The original filename if it wasn't already used, or the filename with " (n)"
/// added to the name if the original filename is already in use.</returns>
private static string NextUniqueFilename(string fileName, Func<string, bool> inUse)
{
if (!inUse(fileName))
{
// this filename has not been seen before, return it unmodified
return fileName;
}
// this filename is already in use, add " (n)" to the end
var name = Path.GetFileNameWithoutExtension(fileName);
var extension = Path.GetExtension(fileName);
if (name == null)
{
throw new Exception("File name without extension returned null.");
}
const int max = 9999;
for (var i = 1; i < max; i++)
{
var nextUniqueFilename = string.Format("{0} ({1}){2}", name, i, extension);
if (!inUse(nextUniqueFilename))
{
return nextUniqueFilename;
}
}
throw new Exception(string.Format("Too many files by this name. Limit: {0}", max));
}
そして、ここでは、ファイルシステムを使用している場合、あなたはそれを呼び出す可能性がある方法です。
var safeName = NextUniqueFilename(filename, f => File.Exists(Path.Combine(folder, f)));
これは単なる文字列操作です。あなたが番号を挿入し、挿入された番号を持つ新しい文字列を再構築したいファイル名の文字列内の場所を見つけます。それは再利用可能にするために、あなたがしたい場合があります。ののを探してその場所に数、そしてあなたがそれをインクリメントすることができますので、整数にそれを解析します。
この一般的でユニークなファイル名を生成するこの方法は安全ではないことに注意してください。明らか競合状態の危険性があります。
プラットフォームでは、このための既製のソリューションがあるかもしれません、私はそこに助けることができないので、私はC#での速度までないんだけど。
パス>クラス、具体的には Path.GetFileNameWithoutExtension()、および Path.GetExtension()。
あなたも、 Path.GetRandomFileNameかもしれません() に便利!
の編集の
過去には、私は(私の希望する名前を持つ)ファイルを書き込もうとし、適切なIOException
が成功するまで繰り返し、スローされた場合は、新しい名前を作成するために、上記の関数を使用する技術を使用しました。
必要な場合は、この方法は、既存のファイルにインデックスを追加します。
ファイルが存在する場合は、、最後のアンダースコアの位置を見つけます。アンダースコアの後にコンテンツが多数ある場合は、この数を増やします。それ以外の場合は最初のインデックスを追加します。見つかった未使用のファイル名になるまで繰り返します。
static public string AddIndexToFileNameIfNeeded(string sFileNameWithPath)
{
string sFileNameWithIndex = sFileNameWithPath;
while (File.Exists(sFileNameWithIndex)) // run in while scoop so if after adding an index the the file name the new file name exist, run again until find a unused file name
{ // File exist, need to add index
string sFilePath = Path.GetDirectoryName(sFileNameWithIndex);
string sFileName = Path.GetFileNameWithoutExtension(sFileNameWithIndex);
string sFileExtension = Path.GetExtension(sFileNameWithIndex);
if (sFileName.Contains('_'))
{ // Need to increase the existing index by one or add first index
int iIndexOfUnderscore = sFileName.LastIndexOf('_');
string sContentAfterUnderscore = sFileName.Substring(iIndexOfUnderscore + 1);
// check if content after last underscore is a number, if so increase index by one, if not add the number _01
int iCurrentIndex;
bool bIsContentAfterLastUnderscoreIsNumber = int.TryParse(sContentAfterUnderscore, out iCurrentIndex);
if (bIsContentAfterLastUnderscoreIsNumber)
{
iCurrentIndex++;
string sContentBeforUnderscore = sFileName.Substring(0, iIndexOfUnderscore);
sFileName = sContentBeforUnderscore + "_" + iCurrentIndex.ToString("000");
sFileNameWithIndex = sFilePath + "\\" + sFileName + sFileExtension;
}
else
{
sFileNameWithIndex = sFilePath + "\\" + sFileName + "_001" + sFileExtension;
}
}
else
{ // No underscore in file name. Simple add first index
sFileNameWithIndex = sFilePath + "\\" + sFileName + "_001" + sFileExtension;
}
}
return sFileNameWithIndex;
}
private async Task<CloudBlockBlob> CreateBlockBlob(CloudBlobContainer container, string blobNameToCreate)
{
var blockBlob = container.GetBlockBlobReference(blobNameToCreate);
var i = 1;
while (await blockBlob.ExistsAsync())
{
var newBlobNameToCreate = CreateRandomFileName(blobNameToCreate,i.ToString());
blockBlob = container.GetBlockBlobReference(newBlobNameToCreate);
i++;
}
return blockBlob;
}
private string CreateRandomFileName(string fileNameWithExtension, string prefix=null)
{
int fileExtPos = fileNameWithExtension.LastIndexOf(".", StringComparison.Ordinal);
if (fileExtPos >= 0)
{
var ext = fileNameWithExtension.Substring(fileExtPos, fileNameWithExtension.Length - fileExtPos);
var fileName = fileNameWithExtension.Substring(0, fileExtPos);
return String.Format("{0}_{1}{2}", fileName, String.IsNullOrWhiteSpace(prefix) ? new Random().Next(int.MinValue, int.MaxValue).ToString():prefix,ext);
}
//This means there is no Extension for the file and its fine attaching random number at the end.
return String.Format("{0}_{1}", fileNameWithExtension, new Random().Next(int.MinValue, int.MaxValue));
}
私は連続し_1、_2、_3など。ファイル名毎回ファイルをBLOBストレージに存在を作成するには、このコードを使用します。
は、この自己反復機能が役立つことを願っています。それは私のために正常に動作します。
public string getUniqueFileName(int i, string filepath, string filename)
{
string path = Path.Combine(filepath, filename);
if (System.IO.File.Exists(path))
{
string name = Path.GetFileNameWithoutExtension(filename);
string ext = Path.GetExtension(filename);
i++;
filename = getUniqueFileName(i, filepath, name + "_" + i + ext);
}
return filename;
}
私はこのようにそれをやった。
for (int i = 0; i <= 500; i++) //I suppose the number of files will not pass 500
{ //Checks if C:\log\log+TheNumberOfTheFile+.txt exists...
if (System.IO.File.Exists(@"C:\log\log"+conta_logs+".txt"))
{
conta_logs++;//If exists, then increment the counter
}
else
{ //If not, then the file is created
var file = System.IO.File.Create(@"C:\log\log" + conta_logs + ".txt");
break; //When the file is created we LEAVE the *for* loop
}
}
私は、このバージョンは、他の人のようにそれほど難しいことではありません、そしてそれは、ユーザーが望んでいた何のために簡単な答えだと思います。
あなただけのユニークなファイル名が必要な場合は、そう、これはどう?
Path.GetRandomFileName()