C #: Wie würden Sie einen eindeutigen Dateinamen, indem Sie eine Nummer hinzufügen?

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

  •  21-08-2019
  •  | 
  •  

Frage

Ich möchte ein Verfahren schaffen, die entweder einen Dateinamen als string oder FileInfo nimmt und fügt eine fortlaufende Nummer an den Dateinamen, wenn die Datei vorhanden ist. Aber kann nicht ganz meinen Kopf wickelt um, wie dies zu tun in einem guten Weg.

Zum Beispiel, wenn ich dieses Fileinfo habe

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

Ich kann das Verfahren wie ich einen neuen Fileinfo mit C: \ Datei 1.ext , wenn C: \ file.ext existierte, und C: \ Datei 2.ext , wenn C: \ Datei 1.ext existierte und so weiter. So etwas wie folgt aus:

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.
}
War es hilfreich?

Lösung 2

Es gibt viele gute Ratschläge hier. Ich landete mit einem Verfahren geschrieben von Marc in eine Antwort auf eine andere Frage . Umformatiert es ein klein wenig und fügte hinzu, eine andere Methode, um es ein bisschen leichter zu machen „von außen“ zu verwenden. Hier ist das Ergebnis:

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

Nur es teilweise so weit getestet, aber aktualisiert, wenn ich irgendwelche Fehler mit ihm finden. ( Marc s Code funktioniert gut!) Wenn Sie irgendwelche Probleme mit ihm finden, bitte kommentieren oder bearbeiten oder etwas :)

Andere Tipps

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

Dies ist offensichtlich anfällig für Race Conditions wie in anderen Antworten zur Kenntnis genommen.

Nicht schön, aber ich habe dies hatte für eine Weile:

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

Unter der Annahme, die Dateien bereits vorhanden ist:

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

den Anruf getNextFileName ( "File.txt") wird wieder auf "Datei (3) .txt".

nicht die effizienteste, weil es nicht binäre Suche nicht verwendet, soll aber für kleine Dateianzahl in Ordnung sein. Und es dauert nicht Race-Bedingung in Betracht ...

Wenn die Überprüfung, ob die Datei vorhanden ist zu hart ist, kann man immer nur hinzufügen, ein Datum und die Uhrzeit an den Dateinamen, um es einzigartig:

FileName.YYYYMMDD.HHMMSS

Vielleicht sogar hinzufügen Millisekunden, falls erforderlich.

Wenn das Format nicht stört, dann können Sie anrufen:

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: - Bitte lesen Sie mehr darüber unter Msdn , bevor Sie.

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

Wie DateTime.Ticks hat eine Auflösung von 100 ns , Kollisionen sind extrem unwahrscheinlich. Jedoch ist ein Thread.Sleep (1) wird sichergestellt, dass aber ich bezweifle, dass es nötig ist

Legen Sie eine neue GUID in den Dateinamen.

Die Idee ist, eine Liste der vorhandenen Dateien zu erhalten, die Zahlen analysieren, dann die nächsthöhere machen.

Hinweis: Dies ist anfällig für Race Conditions, also wenn Sie mehr als einen Thread Erstellen dieser Dateien haben, vorsichtig sein

.

Hinweis 2:. Dies ist nicht getestet

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

Statt die Platte mehrere Male von Stossen um herauszufinden, ob es eine bestimmte Variante des gewünschten Dateinamen hat, könnten Sie für die Liste der Dateien fragen, die bereits existieren und finden Sie den ersten Spalt nach Ihren Algorithmus.

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

Hier ist eine, die die nummerierte Namensgebung Frage von der Prüfung des Dateisystems abkoppelt:

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

Und hier ist, wie man es nennen könnte, wenn Sie das Dateisystem verwenden

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

Dies ist nur ein String-Operation; Suchen Sie den Speicherort im Dateinamen Zeichenfolge in dem die Nummer eingefügt werden soll, und neu zu konstruieren eingefügt, um eine neue Zeichenfolge mit der Nummer. Um es wieder verwendbar, könnten Sie wollen sucht eine Zahl in dieser Position, und analysieren sie in eine ganze Zahl aus, so dass Sie es erhöhen kann.

Bitte beachten Sie, dass dies in der Regel auf diese Weise einen eindeutigen Dateinamen zu erzeugen, ist unsicher; gibt es offensichtliche Race-Bedingung.

Es könnte sein, fertige Lösungen für diese in der Plattform, ich bin oben nicht mit C # zu beschleunigen, damit ich nicht helfen kann.

Werfen Sie einen Blick auf die Methoden in der Pfad rel="nofollow Klasse, speziell Path.GetFileNameWithoutExtension () und Path.GetExtension () .

Sie können sogar finden Path.GetRandomFileName () nützlich!

Edit:

In der Vergangenheit habe ich die Technik des Versuch verwendet, um die Datei zu schreiben (mit meinem gewünschten Namen), und dann die oben genannten Funktionen mit einem neuen Namen erstellen, wenn eine entsprechende IOException geworfen wird, bis er erfolgreich wiederholen.

Diese Methode wird ein Index für bestehende Datei hinzufügen, wenn erforderlich:

Wenn die Datei vorhanden ist, die Position des letzten Unterstrich finden. Wenn der Inhalt nach dem Unterstrich eine Zahl ist, erhöhen Sie diese Zahl. andernfalls ersten Index hinzuzufügen. wiederholen, bis nicht verwendete Dateinamen gefunden.

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

Ich benutze diesen Code ein Mal in Folge zu schaffen _1, _2, _3 etc .. Dateiname jedes Mal eine Datei existiert in dem Blob Speicher.

Hope diese Selbst Iterieren Funktion helfen können. Es funktioniert gut für mich.

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

Ich habe es wie folgt aus:

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

Ich denke, diese Version nicht so hart wie die andere, und es ist eine einfache Antwort für das, was der Benutzer wollte.

Wenn Sie nur einen eindeutigen Dateinamen benötigen, so, wie wäre das?

Path.GetRandomFileName()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top