55

stringファイル名を aまたは aとして受け取りFileInfo、ファイルが存在する場合はファイル名に増分番号を追加するメソッドを作成したいと思います。しかし、これを良い方法で行う方法について頭を悩ませることはできません。

たとえば、この FileInfo がある場合

var file = new FileInfo(@"C:\file.ext");

C:\file.ext が存在する場合は C: \file 1.extを、C:\file 1.extが存在する場合は C:\file 2.extなどの新しい FileInfo をメソッドに提供してもらいたいと考えています。このようなもの:

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.
}
4

20 に答える 20

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

明らかに、他の回答で述べたように、これは競合状態に対して脆弱です。

于 2009-07-03T06:39:15.153 に答える
42

ここにはたくさんの良いアドバイスがあります。別の質問への回答でMarcによって書かれた方法を使用することになりました。少し再フォーマットし、別の方法を追加して、「外部から」使いやすくしました。結果は次のとおりです。

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

これまでのところ部分的にしかテストしていませんが、バグが見つかった場合は更新します。( Marcのコードは問題なく動作します!) 問題が見つかった場合は、コメントまたは編集などでお知らせください :)

于 2009-07-03T11:15:16.477 に答える
12

ファイルが存在するかどうかを確認するのが難しい場合は、いつでもファイル名に日付と時刻を追加して一意にすることができます。

ファイル名.YYYYMMDD.HHMMSS

必要に応じてミリ秒を追加することもできます。

于 2009-07-03T06:40:31.673 に答える
5

フォーマットが気にならない場合は、次のように呼び出すことができます。

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で詳細をお読みください。

于 2009-07-03T06:42:26.163 に答える
4
/// <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) はそれを保証しますが、それが必要かどうかは疑問です

于 2009-07-03T11:32:48.757 に答える
2

ファイル名に新しい GUID を挿入します。

于 2009-07-03T06:47:10.713 に答える
1

アイデアは、既存のファイルのリストを取得し、数値を解析してから、次に高いファイルを作成することです。

注:これは競合状態に対して脆弱であるため、これらのファイルを作成するスレッドが複数ある場合は注意が必要です。

注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));
}
于 2009-07-03T07:02:22.050 に答える
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;
    }
}    
于 2009-07-04T17:41:44.077 に答える
1

ファイルシステムのチェックから番号付けされた命名の質問を切り離すものを次に示します。

/// <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)));
于 2012-03-21T14:34:39.073 に答える
1
    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 などのファイル名を作成します。

于 2015-10-02T16:56:17.460 に答える
1

これはこのリンクの質問への回答ですが、重複としてマークされているため、ここに回答を投稿します。

この概念実証クラスを作成しました (バグが含まれている可能性があります)。コード コメントの詳細説明。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace ConsoleApp
{
    class Program
    {
        static void Main( string[] args )
        {
            var testFilePaths = new List<string>
            {
                @"c:\test\file.txt",
                @"c:\test\file(1).txt",
                @"c:\test\file(2).txt",
                @"c:\TEST2\file(3).txt",
                @"c:\test\file(5).txt",
                @"c:\test\file(5)abc.txt",
                @"c:\test\file(5).avi"
            };

            // inspect in debbuger for correct values
            var withSuffix      = new DecomposedFilePath( "c:\\files\\file(13).txt");
            var withoutSuffix   = new DecomposedFilePath( "c:\\files\\file(abc).txt");
            var withExtraNumber = new DecomposedFilePath( "c:\\files\\file(34)xyz(35).txt"); // "(34)" in the middle should be ignored

            DecomposedFilePath changedSuffix = withExtraNumber.ReplaceSuffix( 1999 ); // "file(34)xyz(35).txt" -> "file(34)xyz(1999).txt"
            DecomposedFilePath removedSuffix = changedSuffix.ReplaceSuffix( null ); // "file(34)xyz(1999).txt" -> "file(34)xyz.txt"

            var testPath = new DecomposedFilePath( "c:\\test\\file.txt");
            DecomposedFilePath nextPath1 = testPath.GetFirstFreeFilePath( testFilePaths );

            // update our list
            testFilePaths.Add( nextPath1.FullFilePath );
            DecomposedFilePath nextPath2 = testPath.GetFirstFreeFilePath( testFilePaths );
        
            testFilePaths.Add( nextPath2.FullFilePath );
            DecomposedFilePath nextPath3 = testPath.GetFirstFreeFilePath( testFilePaths );
        }
    }

    public sealed class DecomposedFilePath
    {
        public DecomposedFilePath( string filePath )
        {
            FullFilePath = Path.GetFullPath( filePath );
        }

        // "c:\myfiles\file(4).txt"
        public string FullFilePath { get; }

        // "file" or "file(1)"
        public string FileNameWithoutExt => Path.GetFileNameWithoutExtension( FullFilePath );

        // "file(13)" -> "file"
        public string FileNameWithoutExtAndSuffix => FileNameWithoutExt.Substring( 0, FileNameWithoutExt.Length - Suffix.Length ); // removes suffix

        // ".txt"
        public string Extenstion => Path.GetExtension( FullFilePath );

        // "c:\myfiles"
        public string DirectoryPath => Path.GetDirectoryName( FullFilePath );

        // "file(23)" -> "23", file -> stirng.Empty
        public string Suffix
        {
            get
            {
                // we want to extract suffix from file name, e.g. "(34)" from "file(34)"
                // I am not good at regex, but I hope it will work correctly

                var regex = new Regex( @"\([0-9]+\)$" );
                Match match = regex.Match( FileNameWithoutExt );

                if (!match.Success) return string.Empty; // suffix not found

                return match.Value; // return "(number)"
            }
        }

        // tranlates suffix "(33)" to 33. If suffix is does not exist (string.empty), returns null (int?)
        public int? SuffixAsInt
        {
            get
            {
                if (Suffix == string.Empty) return null;

                string numberOnly = Suffix.Substring( 1, Suffix.Length - 2 ); // remove '(' from beginning and ')' from end

                return int.Parse( numberOnly );
            }
        }

        // e.g. input is suffix: 56 then it changes file name from "file(34)" to "file(56)"
        public DecomposedFilePath ReplaceSuffix( int? suffix ) // null - removes suffix
        {
            string strSuffix = suffix is null ? string.Empty : $"({suffix})"; // add ( and )

            string path = Path.Combine( DirectoryPath, FileNameWithoutExtAndSuffix + strSuffix + Extenstion ); // build full path

            return new DecomposedFilePath( path );
        }

        public DecomposedFilePath GetFirstFreeFilePath( IEnumerable<string> filesInDir )
        {
            var decomposed = filesInDir
                // convert all paths to our class
                .Select( x => new DecomposedFilePath( x ) )
                // pick files only with the same extensionm as our base file, ignore case
                .Where( x => string.Equals( Extenstion, x.Extenstion, StringComparison.OrdinalIgnoreCase) )
                // pick files only with the same name (ignoring suffix)
                .Where( x => string.Equals( FileNameWithoutExtAndSuffix, x.FileNameWithoutExtAndSuffix, StringComparison.OrdinalIgnoreCase) )
                // with the same directory
                .Where( x => string.Equals( DirectoryPath, x.DirectoryPath, StringComparison.OrdinalIgnoreCase) )
                .ToList(); // create copy for easier debugging

            if (decomposed.Count == 0) return this; // no name collision

            int? firstFreeSuffix = Enumerable.Range( 1, int.MaxValue) // start numbering duplicates from 1
                                  .Select( x => (int?) x) // change to int? because SuffixAsInt is of that type
                                  .Except( decomposed.Select( x => x.SuffixAsInt) ) // remove existing suffixes
                                  .First(); // get first free suffix

            return ReplaceSuffix( firstFreeSuffix );
        }

        public override string ToString() => FullFilePath;
    }
}
于 2020-10-17T12:15:21.470 に答える
0

これは単なる文字列操作です。番号を挿入するファイル名文字列の場所を見つけ、番号が挿入された新しい文字列を再構築します。再利用可能にするために、その場所で数値を探し、それを解析して整数に変換し、インクリメントできるようにすることができます。

一般に、一意のファイル名を生成するこの方法は安全ではないことに注意してください。明らかな競合状態の危険があります。

プラットフォームにはこれに対する既製のソリューションがあるかもしれません。

于 2009-07-03T06:37:29.070 に答える
0

Pathクラスのメソッド、具体的にはPath.GetFileNameWithoutExtension()Path.GetExtension()を見てください。

Path.GetRandomFileName()が便利な場合もあります。

編集:

過去に、ファイルを(目的の名前で)書き込もうとする手法を使用し、適切な名前IOExceptionがスローされた場合は上記の関数を使用して新しい名前を作成し、成功するまで繰り返しました。

于 2009-07-03T06:38:49.147 に答える
0

私はこのようにしました:

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

このバージョンは他のバージョンほど難しいものではなく、ユーザーが望んでいたことに対する率直な答えだと思います。

于 2015-02-28T21:49:03.813 に答える
0

一意のファイル名だけが必要な場合は、これはどうですか?

Path.GetRandomFileName()
于 2018-11-06T05:36:59.360 に答える