Domanda

Vorrei creare un metodo che prende sia un nome di file come o un string FileInfo e aggiunge un numero incrementato al nome del file, se il file esiste. Ma non si può del tutto avvolgere la mia testa intorno a come farlo nel modo giusto.

Per esempio, se ho questo FileInfo

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

Vorrei il metodo di darmi una nuova FileInfo con file C: \ 1.ext se C: \ file.ext esisteva, e file C: \ 2.ext se file C: \ 1.ext esisteva e così via. Qualcosa di simile a questo:

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.
}
È stato utile?

Soluzione 2

Un sacco di buoni consigli qui. Ho finito per usare un metodo scritto da Marc in una risposta a una domanda diversa . Riformattato un po 'piccolo e ha aggiunto un altro metodo per renderlo un po' più facile da usare "dall'esterno". Ecco il risultato:

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

Solo parzialmente testato finora, ma verrà aggiornato se trovo qualche bug con esso. ( codice di Marc s funziona bene!) Se avete dei problemi con esso, si prega di commentare o modificare o qualcosa :)

Altri suggerimenti

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

Ovviamente, questo è vulnerabile a condizioni di gara, come già sottolineato in altre risposte.

Non è abbastanza, ma ho avuto per un po ':

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

Supponendo che esistono già dei file:

  • File.txt
  • File (1) .txt
  • File (2) .txt

il getNextFileName chiamata ( "file.txt") restituisce "File (3) .txt".

Non è il più efficiente perché non usa la ricerca binaria, ma dovrebbe essere ok per numero di file di piccole dimensioni. E non ci vuole race condition in considerazione ...

Se la verifica se il file esiste è troppo duro si può sempre e solo aggiungere una data e l'ora al nome del file per renderlo unico nel suo genere:

FileName.YYYYMMDD.HHMMSS

Forse anche aggiungere millisecondi, se necessario.

Se il formato non vi da fastidio, allora si può chiamare:

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: - Si prega di leggere di più su questo a MSDN prima di utilizzare.

/// <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 ha una risoluzione di 100 nanosecondi , le collisioni sono estremamente improbabile. Tuttavia, un Thread.Sleep (1) farà in modo che, ma dubito che ce n'è bisogno

Inserire un nuovo GUID nel nome del file.

L'idea è quella di ottenere un elenco dei file esistenti, analizzare i numeri, poi fare il successivo più alto.

Nota: Questo è vulnerabile a condizioni di gara, quindi se avete più di un thread creazione di questi file, fare attenzione

.

Nota 2:. Questa non è testato

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

Invece di colpendo il disco di un certo numero di volte per scoprire se si ha una particolare variante del nome del file desiderato, si può chiedere l'elenco dei file che già esistono e trovare il primo gap in base al vostro algoritmo.

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

Ecco uno che disaccoppia la domanda di denominazione numerata dal controllo del filesystem:

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

Ed ecco come si potrebbe chiamare se si utilizza il file system

var safeName = NextUniqueFilename(filename, f => File.Exists(Path.Combine(folder, f)));

Questa è solo un'operazione di stringa; trovare la posizione nella stringa di nome di file in cui si desidera inserire il numero, e ri-costruire una nuova stringa con il numero inserito. Per renderlo riutilizzabile, si potrebbe desiderare di cercare un numero in quella posizione, e analizzare fuori in un intero, in modo da poter incrementarlo.

Si prega di notare che questo, in generale, questo modo di generare un nome di file unico è insicuro; ci sono evidenti race condition .

Ci potrebbe essere già pronti soluzioni per questo nella piattaforma, non sono al passo con C # in modo da non posso fare lì.

Date un'occhiata ai metodi nella Path di classe, in particolare Path.GetFileNameWithoutExtension () e Path.GetExtension () .

Si può anche trovare Path.GetRandomFileName () utile!

Modifica

In passato, ho usato la tecnica di tentare di scrivere il file (con il mio nome desiderato), e quindi utilizzando le funzioni di cui sopra per creare un nuovo nome se un IOException appropriata è gettata, ripetendo fino successo.

Questo metodo aggiungerà un indice per il file se necessario:

Se il file esiste, trovare la posizione dell'ultimo sottolineatura. Se il contenuto dopo che la sottolineatura è un numero, aumentare questo numero. altrimenti aggiungere primo indice. ripetere fino a quando il nome del file inutilizzato trovato.

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

Io uso questo codice per creare un consecutivo _1, _2, _3 ecc .. nome di file ogni volta che un file esiste nella memoria blob.

La speranza questa funzione l'iterazione di auto può aiutare. Funziona bene per me.

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

L'ho fatto in questo modo:

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

Credo che questa versione non è così difficile come gli altri, ed è una risposta diretta a ciò che l'utente voleva.

Se avete bisogno di solo un nome di file unico, quindi, come su questo?

Path.GetRandomFileName()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top