씨#:숫자를 추가하여 고유한 파일 이름을 만드는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1078003

  •  21-08-2019
  •  | 
  •  

문제

파일 이름을 다음 중 하나로 사용하는 메서드를 만들고 싶습니다. 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

여기에 많은 좋은 조언이 있습니다. 나는 작성된 메소드를 사용하여 끝났다 마크 안에 다른 질문에 대한 답. 그것을 조금씩 재구성하고 다른 방법을 추가하여 "외부에서"사용하기가 조금 더 쉬워졌습니다. 결과는 다음과 같습니다.

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

파일이 이미 존재한다고 가정합니다.

  • file.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
}

추신 :- 이것에 대해 자세히 알아보십시오 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 나노초입니다, 충돌은 거의 가능하지 않습니다. 그러나 실로 (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));
}

원하는 파일 이름의 특정 변형이 있는지 확인하기 위해 디스크를 여러 번 찌르는 대신 이미 존재하는 파일 목록을 요청하고 알고리즘에 따라 첫 번째 간격을 찾을 수 있습니다.

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 등을 만듭니다. 파일이 블로브 스토리지에 파일이 존재할 때마다 파일 이름입니다.

이 자체 반복 기능이 도움이되기를 바랍니다. 그것은 나를 위해 잘 작동합니다.

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()
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top