C#: How would you make a unique filename by adding a number?

前端 未结 18 826
没有蜡笔的小新
没有蜡笔的小新 2020-11-27 11:46

I would like to create a method which takes either a filename as a string or a FileInfo and adds an incremented number to the filename if the file

18条回答
  •  不知归路
    2020-11-27 12:05

    This is an answer to question in this Link, but they marked it as a duplicate, so I post my answer here.

    I created this proof of concept class (may contain bugs). More explanation in code comments.

    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
                {
                    @"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 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;
        }
    }
    

提交回复
热议问题