So generieren Sie eindeutige Dateinamen in C #


131

Ich habe einen Algorithmus implementiert, der eindeutige Namen für Dateien generiert, die auf der Festplatte gespeichert werden. Ich füge hinzu DateTime: Stunden, Minuten, Sekunden und Millisekunden, aber es werden immer noch doppelte Namen von Dateien generiert, da ich mehrere Dateien gleichzeitig hochlade.

Was ist die beste Lösung, um eindeutige Namen für Dateien zu generieren, die auf der Festplatte gespeichert werden sollen, damit keine zwei Dateien gleich sind?


Es hängt von anderen Anforderungen ab; Diese [alte] Frage war / ist zu vage.
user2864740

Antworten:


240

Wenn die Lesbarkeit keine Rolle spielt, verwenden Sie GUIDs .

Z.B:

var myUniqueFileName = string.Format(@"{0}.txt", Guid.NewGuid());

oder kürzer :

var myUniqueFileName = $@"{Guid.NewGuid()}.txt";

In meinen Programmen versuche ich manchmal 10 Mal, einen lesbaren Namen zu generieren ("Image1.png" ... "Image10.png"), und wenn dies fehlschlägt (weil die Datei bereits vorhanden ist), greife ich auf GUIDs zurück.

Aktualisieren:

In letzter Zeit habe ich auch DateTime.Now.Ticksanstelle von GUIDs verwendet:

var myUniqueFileName = string.Format(@"{0}.txt", DateTime.Now.Ticks);

oder

var myUniqueFileName = $@"{DateTime.Now.Ticks}.txt";

Der Vorteil für mich ist, dass dies im Vergleich zu GUIDs einen kürzeren und "besser aussehenden" Dateinamen erzeugt.

Bitte beachten Sie, dass in einigen Fällen (zB wenn eine Menge von zufälligen Namen in sehr kurzen Zeit zu erzeugen), dies könnte nicht eindeutige Werte machen.

Halten Sie sich an GUIDs, wenn Sie wirklich sicherstellen möchten, dass die Dateinamen eindeutig sind, auch wenn Sie sie auf andere Computer übertragen.


7
Ich mag es, Ticks als GUID zu verwenden, das ist wirklich hässlich. Sie können auch einen Hash der Ticks erhalten, wodurch die Zeichenlänge des Dateinamens verringert wird. DateTime.Now.Ticks.GetHashCode().ToString("x").ToUpper()
WillMcKill

4
"Ticks" sind vorhersehbar und nicht threadsicher (da dieselben "Ticks" von mehreren Threads / Prozessen erhalten werden können). Dies macht es nicht für die temporäre Dateinamengenerierung geeignet. Das Generieren von X..1..N eignet sich möglicherweise für benutzerbezogene Aufgaben (z. B. Kopieren im Explorer), ist jedoch für die Serverarbeit zweifelhaft.
user2864740

90

Verwenden

Path.GetTempFileName()

oder verwenden Sie eine neue GUID ().

Path.GetTempFilename () auf MSDN .


Hier ist der Link zum MSDN-Dokument: msdn.microsoft.com/en-us/library/…
Epotter

3
Beachten Sie jedoch, dass dies GetTempFileName()eine Ausnahme auslösen kann, wenn Sie viele solcher Dateien erstellen, ohne sie zu löschen.
Joey

21
"Die GetTempFileName-Methode löst eine IOException aus, wenn damit mehr als 65535 Dateien erstellt werden, ohne vorherige temporäre Dateien zu löschen." sagt der MSDN-Artikel.
Çağdaş Tekin

1
ACHTUNG: GetTempFileNamewird erstellen eine Datei. Dies bedeutet auch, dass der temporäre Pfad ausgewählt wird . Andererseits GetRandomFileNameist es geeignet, einen 8.3- Dateinamen zu generieren , der mit einem anderen Pfad verwendet werden kann. (Ich habe einen schrecklichen Code gesehen, der GetTempFileName mit einer Datei verwendet. Löschen, nur um den Dateinamen an anderer Stelle zu verwenden.)
user2864740


54

Wenn die Lesbarkeit des Dateinamens nicht wichtig ist, reicht die von vielen vorgeschlagene GUID aus. Ich finde jedoch, dass das Durchsuchen eines Verzeichnisses mit 1000 GUID-Dateinamen sehr entmutigend ist. Daher verwende ich normalerweise eine Kombination aus einer statischen Zeichenfolge, die dem Dateinamen einige Kontextinformationen, einen Zeitstempel und eine GUID gibt.

Beispielsweise:

public string GenerateFileName(string context)
{
    return context + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + Guid.NewGuid().ToString("N");
}

filename1 = GenerateFileName("MeasurementData");
filename2 = GenerateFileName("Image");

Auf diese Weise werden beim Sortieren nach Dateinamen die Dateien automatisch nach der Kontextzeichenfolge gruppiert und nach Zeitstempel sortiert.

Beachten Sie, dass das Dateinamenlimit in Windows 255 Zeichen beträgt.


1
+1 Für den Vorschlag, nützliche Informationen in Kombination mit einer GUID aufzunehmen. - Mit einem Körnchen Salz beiseite nehmen: Das Einfügen von Datum und Uhrzeit in einen Dateinamen ist überflüssig, wenn Sie nur können Right Click > Sort By > Date.
Timothy Shields

1
Die Zeit wird nützlich, wenn Sie eine Reihe von Dateien mit unterschiedlichen Kontexten im selben Verzeichnis speichern. Natürlich sollte die Generierung des Dateinamens an Ihre spezifischen Anforderungen angepasst werden.
Mas

Sollte sein Guid.NewGuid().ToString();. Fehlende Klammer. +1 sonst
Laurent W.

Das ist sehr schlau. Zeitstempel und Guid. +1
JoshYates1980

Ich mag diese Lösung +1, ich habe eine zweite Parameterzeichenfolgenerweiterung hinzugefügt, die ich dem Dateinamen hinzufüge. Dies unterstützt die Idee des Kontexts weiter und ermöglicht das einfache Öffnen der Datei mit der Standardanwendung per Doppelklick, falls erforderlich
Shelbypereira

23

Hier ist ein Algorithmus, der einen eindeutigen lesbaren Dateinamen basierend auf dem mitgelieferten Original zurückgibt. Wenn die Originaldatei vorhanden ist, wird schrittweise versucht, einen Index an den Dateinamen anzuhängen, bis ein nicht vorhandener Index gefunden wird. Es liest die vorhandenen Dateinamen in ein HashSet, um nach Kollisionen zu suchen, so dass es ziemlich schnell ist (ein paar hundert Dateinamen pro Sekunde auf meinem Computer), auch threadsicher ist und nicht unter Rennbedingungen leidet.

Wenn Sie es beispielsweise übergeben test.txt, wird versucht, Dateien in dieser Reihenfolge zu erstellen:

test.txt
test (2).txt
test (3).txt

usw. Sie können die maximalen Versuche angeben oder die Standardeinstellung beibehalten.

Hier ist ein vollständiges Beispiel:

class Program
{
    static FileStream CreateFileWithUniqueName(string folder, string fileName, 
        int maxAttempts = 1024)
    {
        // get filename base and extension
        var fileBase = Path.GetFileNameWithoutExtension(fileName);
        var ext = Path.GetExtension(fileName);
        // build hash set of filenames for performance
        var files = new HashSet<string>(Directory.GetFiles(folder));

        for (var index = 0; index < maxAttempts; index++)
        {
            // first try with the original filename, else try incrementally adding an index
            var name = (index == 0)
                ? fileName
                : String.Format("{0} ({1}){2}", fileBase, index, ext);

            // check if exists
            var fullPath = Path.Combine(folder, name);
            if(files.Contains(fullPath))
                continue;

            // try to create the file
            try
            {
                return new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write);
            }
            catch (DirectoryNotFoundException) { throw; }
            catch (DriveNotFoundException) { throw; }
            catch (IOException) 
            {
                // Will occur if another thread created a file with this 
                // name since we created the HashSet. Ignore this and just
                // try with the next filename.
            } 
        }

        throw new Exception("Could not create unique filename in " + maxAttempts + " attempts");
    }

    static void Main(string[] args)
    {
        for (var i = 0; i < 500; i++)
        {
            using (var stream = CreateFileWithUniqueName(@"c:\temp\", "test.txt"))
            {
                Console.WriteLine("Created \"" + stream.Name + "\"");
            }
        }

        Console.ReadKey();
    }
}

Thread-sicher ? nichtstatic readonly Variable wederlock?
Kiquenet

Die Methode selbst ist statisch, teilt also nichts, daher glaube ich, dass mehrere Threads sicher gleichzeitig in diese Methode eintreten können. Vielleicht ist thread-safe nicht ganz der richtige Begriff - ich versuche zu vermitteln, dass diese Methode den nächsten verfügbaren Namen wiederherstellt und versucht, wenn ein anderer Thread / Prozess während der Ausführung eine Datei mit einem widersprüchlichen Namen erstellt. Fühlen Sie sich frei zu bearbeiten, wenn Sie denken, dass es verbessert werden kann.
Mike Chamberlain

Vielleicht ist "nicht unter einer Rennbedingung leiden" eine bessere Art, es auszudrücken.
Mike Chamberlain

10

Ich benutze GetRandomFileName :

Die GetRandomFileName-Methode gibt eine kryptografisch starke, zufällige Zeichenfolge zurück, die entweder als Ordnername oder als Dateiname verwendet werden kann. Im Gegensatz zu GetTempFileName erstellt GetRandomFileName keine Datei. Wenn die Sicherheit Ihres Dateisystems von größter Bedeutung ist, sollte diese Methode anstelle von GetTempFileName verwendet werden.

Beispiel:

public static string GenerateFileName(string extension="")
{
    return string.Concat(Path.GetRandomFileName().Replace(".", ""),
        (!string.IsNullOrEmpty(extension)) ? (extension.StartsWith(".") ? extension : string.Concat(".", extension)) : "");
}

Generiert die GetRandomFileName () -Methode immer den eindeutigen Dateinamen, ähnlich wie bei GUID ()?
Ashish Shukla

1
@ AshishShukla eigentlich habe ich keine Ahnung. Laut msdn wird "eine kryptografisch starke, zufällige Zeichenfolge" generiert. Ich hatte bisher keine Probleme. Wenn die Eindeutigkeit kritisch ist, ist eine zusätzliche Überprüfung möglicherweise eine gute Idee.
Koray

3
  1. Erstellen Sie Ihren zeitgestempelten Dateinamen wie gewohnt
  2. Überprüfen Sie, ob der Dateiname vorhanden ist
  3. False - Datei speichern
  4. True - Fügen Sie der Datei ein zusätzliches Zeichen hinzu, möglicherweise einen Zähler
  5. Weiter zu Schritt 2

10
Dieser Algorithmus kann nicht gleichzeitig verwendet werden
Jader Dias

3

Sie können einen eindeutigen Dateinamen ohne benutzerdefinierte Methoden automatisch für Sie generieren lassen. Verwenden Sie einfach Folgendes mit der StorageFolder-Klasse oder der StorageFile-Klasse . Der Schlüssel hier ist: CreationCollisionOption.GenerateUniqueNameundNameCollisionOption.GenerateUniqueName

So erstellen Sie eine neue Datei mit einem eindeutigen Dateinamen:

var myFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("myfile.txt", NameCollisionOption.GenerateUniqueName);

So kopieren Sie eine Datei an einen Speicherort mit einem eindeutigen Dateinamen:

var myFile2 = await myFile1.CopyAsync(ApplicationData.Current.LocalFolder, myFile1.Name, NameCollisionOption.GenerateUniqueName);

So verschieben Sie eine Datei mit einem eindeutigen Dateinamen am Zielspeicherort:

await myFile.MoveAsync(ApplicationData.Current.LocalFolder, myFile.Name, NameCollisionOption.GenerateUniqueName);

So benennen Sie eine Datei mit einem eindeutigen Dateinamen am Zielspeicherort um:

await myFile.RenameAsync(myFile.Name, NameCollisionOption.GenerateUniqueName);

2

Ich habe den folgenden Code verwendet und er funktioniert einwandfrei. Ich hoffe das könnte dir helfen.

Ich beginne mit einem eindeutigen Dateinamen unter Verwendung eines Zeitstempels -

"context_" + DateTime.Now.ToString ("yyyyMMddHHmmssffff")

C # -Code -

public static string CreateUniqueFile(string logFilePath, string logFileName, string fileExt)
    {
        try
        {
            int fileNumber = 1;

            //prefix with . if not already provided
            fileExt = (!fileExt.StartsWith(".")) ? "." + fileExt : fileExt;

            //Generate new name
            while (File.Exists(Path.Combine(logFilePath, logFileName + "-" + fileNumber.ToString() + fileExt)))
                fileNumber++;

            //Create empty file, retry until one is created
            while (!CreateNewLogfile(logFilePath, logFileName + "-" + fileNumber.ToString() + fileExt))
                fileNumber++;

            return logFileName + "-" + fileNumber.ToString() + fileExt;
        }
        catch (Exception)
        {
            throw;
        }
    }

    private static bool CreateNewLogfile(string logFilePath, string logFile)
    {
        try
        {
            FileStream fs = new FileStream(Path.Combine(logFilePath, logFile), FileMode.CreateNew);
            fs.Close();
            return true;
        }
        catch (IOException)   //File exists, can not create new
        {
            return false;
        }
        catch (Exception)     //Exception occured
        {
            throw;
        }
    }

1

Benötigen Sie den Datums- / Zeitstempel im Dateinamen?

Sie können den Dateinamen zu einer GUID machen.


@downvoter Gibt es einen Grund für die Abwertung? Eine GUID für einen Dateinamen scheint eine beliebte Antwort auf diese Frage zu sein.
Larry Hipp

Es ist eine wiederholte Antwort, und ich habe nicht genug Ruf, um abzustimmen
Jader Dias

@ XMLforDummies meine Antwort war eine der ersten. Es scheint jetzt vielleicht nicht so, weil es nur die Stunden zeigt. Es ist eine wiederholte Antwort, weil es wahrscheinlich die richtige Antwort ist.
Larry Hipp


1

Wie Guid.NewGuid()wäre es, wenn Sie eine GUID erstellen und diese als Dateinamen verwenden (oder einen Teil des Dateinamens zusammen mit Ihrem Zeitstempel, wenn Sie möchten).


1

Ich habe eine einfache rekursive Funktion geschrieben, die wie Windows Dateinamen generiert, indem eine Sequenznummer vor der Dateierweiterung angehängt wird.

Bei einem gewünschten Dateipfad von C:\MyDir\MyFile.txtund wenn die Datei bereits vorhanden ist, wird ein endgültiger Dateipfad von zurückgegeben C:\MyDir\MyFile_1.txt.

Es heißt so:

var desiredPath = @"C:\MyDir\MyFile.txt";
var finalPath = UniqueFileName(desiredPath);

private static string UniqueFileName(string path, int count = 0)
{
    if (count == 0)
    {
        if (!File.Exists(path))
        {
            return path;
        }
    }
    else
    {
        var candidatePath = string.Format(
            @"{0}\{1}_{2}{3}",
            Path.GetDirectoryName(path),
            Path.GetFileNameWithoutExtension(path),
            count,
            Path.GetExtension(path));

        if (!File.Exists(candidatePath))
        {
            return candidatePath;
        }
    }

    count++;
    return UniqueFileName(path, count);
}

Dies ist nicht thread- oder prozesssicher. Es gibt eine Race-Bedingung mit der File.Exists-Prüfung und jeder (später angenommenen) Erstellung der Datei. Wenn zweimal hintereinander aufgerufen wird, ohne eine Datei zu erstellen, wird das gleiche Ergebnis zurückgegeben.
user2864740

1

Warum können wir keine eindeutige ID wie unten erstellen?

Wir können DateTime.Now.Ticks und Guid.NewGuid (). ToString () verwenden, um sie zu kombinieren und eine eindeutige ID zu erstellen.

Wenn DateTime.Now.Ticks hinzugefügt wird, können wir Datum und Uhrzeit in Sekunden ermitteln, zu denen die eindeutige ID erstellt wird.

Bitte beachten Sie den Code.

var ticks = DateTime.Now.Ticks;
var guid = Guid.NewGuid().ToString();
var uniqueSessionId = ticks.ToString() +'-'+ guid; //guid created by combining ticks and guid

var datetime = new DateTime(ticks);//for checking purpose
var datetimenow = DateTime.Now;    //both these date times are different.

Wir können sogar den Teil der Zecken in einer eindeutigen ID verwenden und später nach Datum und Uhrzeit suchen, um später darauf zurückgreifen zu können.

Sie können die erstellte eindeutige ID an den Dateinamen anhängen oder zum Erstellen einer eindeutigen Sitzungs-ID für das Anmelden und Abmelden von Benutzern bei unserer Anwendung oder Website verwenden.


Warum sich mit Zecken beschäftigen, wenn es eine GUID gibt?
user2864740

Wenn Sie in einem beliebigen Szenario überprüfen müssen, wann die uniqueSessionId generiert wird, erhalten Sie die genaue Zeit. Und auch an diesem bestimmten Punkt wird nur einmal im Leben auftreten.
Jineesh Uvantavida

Trivialerweise ist diese Annahme über Zecken ungültig: 1) Mehrere Beobachter können dasselbe "Häkchen" sehen (denken Sie an Threads / Prozesse) und 2) dasselbe "Häkchen" kann von demselben Beobachter mehrmals beobachtet werden, wenn es schnell genug abgefragt wird.
user2864740

Jedoch durch nur mit Guid.NewGuid (und ignorieren diese Tatsache nicht „kryptografisch random“ ist die in einigen Fällen von Interesse sein könnte), können wir , dass mit einer ausreichend hohen Wahrscheinlichkeit behaupten , dass wir uns nicht um sonst ist es egal, eine eindeutige ID wird generiert werden - dies ist eine viel, viel höhere Garantie als "Zecken" . Daher haben die 'Ticks' hier keinen Wert / keine Verwendung als "sekundäre" Daten, die in den Dateinamen verschoben werden.
user2864740

(FWIW: Ich habe gerade einen Code mit einer kaputten Behauptung über 'eindeutige Zeit'
korrigiert

0

Wenn Sie Datum, Uhrzeit, Stunden, Minuten usw. haben möchten, können Sie eine statische Variable verwenden. Hängen Sie den Wert dieser Variablen an den Dateinamen an. Sie können den Zähler mit 0 starten und erhöhen, wenn Sie eine Datei erstellt haben. Auf diese Weise wird der Dateiname sicherlich eindeutig sein, da Sie auch Sekunden in der Datei haben.


0

Normalerweise mache ich etwas in diese Richtung:

  • Beginnen Sie mit einem Stammdateinamen ( work.dat1zum Beispiel).
  • Versuchen Sie es mit CreateNew zu erstellen
  • Wenn das funktioniert, haben Sie die Datei, sonst ...
  • Mischen Sie das aktuelle Datum und die aktuelle Uhrzeit in den Dateinamen ( work.2011-01-15T112357.datzum Beispiel).
  • Versuchen Sie, die Datei zu erstellen
  • Wenn das funktioniert hat, haben Sie die Datei, sonst ...
  • Mischen Sie einen monotonen Zähler in den Dateinamen ( work.2011-01-15T112357.0001.datzum Beispiel. (Ich mag keine GUIDs. Ich bevorzuge Reihenfolge / Vorhersagbarkeit.)
  • Versuchen Sie, die Datei zu erstellen. Aktivieren Sie den Zähler und versuchen Sie es erneut, bis eine Datei für Sie erstellt wurde.

Hier ist eine Beispielklasse:

static class DirectoryInfoHelpers
{
    public static FileStream CreateFileWithUniqueName( this DirectoryInfo dir , string rootName )
    {
        FileStream fs = dir.TryCreateFile( rootName ) ; // try the simple name first

        // if that didn't work, try mixing in the date/time
        if ( fs == null )
        {
            string date = DateTime.Now.ToString( "yyyy-MM-ddTHHmmss" ) ;
            string stem = Path.GetFileNameWithoutExtension(rootName) ;
            string ext  = Path.GetExtension(rootName) ?? ".dat" ;

            ext = ext.Substring(1);

            string fn = string.Format( "{0}.{1}.{2}" , stem , date , ext ) ;
            fs = dir.TryCreateFile( fn ) ;

            // if mixing in the date/time didn't work, try a sequential search
            if ( fs == null )
            {
                int seq = 0 ;
                do
                {
                    fn = string.Format( "{0}.{1}.{2:0000}.{3}" , stem , date , ++seq , ext ) ;
                    fs = dir.TryCreateFile( fn ) ;
                } while ( fs == null ) ;
            }

        }

        return fs ;
    }

    private static FileStream TryCreateFile(this DirectoryInfo dir , string fileName )
    {
        FileStream fs = null ;
        try
        {
            string fqn = Path.Combine( dir.FullName , fileName ) ;

            fs = new FileStream( fqn , FileMode.CreateNew , FileAccess.ReadWrite , FileShare.None ) ;
        }
        catch ( Exception )
        {
            fs = null ;
        }
        return fs ;
    }

}

Möglicherweise möchten Sie den Algorithmus optimieren (verwenden Sie beispielsweise immer alle möglichen Komponenten für den Dateinamen). Abhängig vom Kontext - Wenn ich beispielsweise Protokolldateien erstellt habe, die möglicherweise nicht mehr vorhanden sind, möchten Sie, dass alle das gleiche Muster für den Namen verwenden.

Der Code ist nicht perfekt (zum Beispiel keine Überprüfung der übergebenen Daten). Und der Algorithmus ist nicht perfekt (wenn Sie beispielsweise die Festplatte füllen oder auf Berechtigungen, tatsächliche E / A-Fehler oder andere Dateisystemfehler stoßen, bleibt dies in einer Endlosschleife hängen).


0

Am Ende verkette ich die GUID mit der Zeichenfolge Tag Monat Jahr Zweite Millisekunde und ich denke, diese Lösung ist in meinem Szenario ziemlich gut



0

Ich habe speziell dafür eine Klasse geschrieben. Es wird mit einem "Basis" -Teil initialisiert (standardmäßig ein minutengenauer Zeitstempel) und anschließend Buchstaben angehängt, um eindeutige Namen zu erstellen. Wenn also der erste erzeugte Stempel 1907101215a ist, wäre der zweite 1907101215b, dann 1907101215c und so weiter.

Wenn ich mehr als 25 eindeutige Briefmarken benötige, verwende ich unäre Zs, um 25 zu zählen. So geht es 1907101215y, 1907101215za, 1907101215zb, ... 1907101215zy, 1907101215zza, 1907101215zzb und so weiter. Dies garantiert, dass die Stempel immer alphanumerisch in der Reihenfolge sortiert werden, in der sie generiert wurden (solange das nächste Zeichen nach dem Stempel kein Buchstabe ist).

Es ist nicht threadsicher, aktualisiert die Zeit nicht automatisch und bläht sich schnell auf, wenn Sie Hunderte von Briefmarken benötigen, aber ich finde es ausreichend für meine Bedürfnisse.

/// <summary>
/// Class for generating unique stamps (for filenames, etc.)
/// </summary>
/// <remarks>
/// Each time ToString() is called, a unique stamp is generated.
/// Stamps are guaranteed to sort alphanumerically in order of generation.
/// </remarks>
public class StampGenerator
{
  /// <summary>
  /// All the characters which could be the last character in the stamp.
  /// </summary>
  private static readonly char[] _trailingChars =
  {
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
    'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
    'u', 'v', 'w', 'x', 'y'
  };

  /// <summary>
  /// How many valid trailing characters there are.
  /// </summary>
  /// <remarks>Should always equal _trailingChars.Length</remarks>
  public const int TRAILING_RANGE = 25;

  /// <summary>
  /// Maximum length of the stamp. Hard-coded for laziness.
  /// </summary>
  public const int MAX_LENGTH_STAMP = 28;

  /// <summary>
  /// Base portion of the stamp. Will be constant between calls.
  /// </summary>
  /// <remarks>
  /// This is intended to uniquely distinguish between instances.
  /// Default behavior is to generate a minute-accurate timestamp.
  /// </remarks>
  public string StampBase { get; }

  /// <summary>
  /// Number of times this instance has been called.
  /// </summary>
  public int CalledTimes { get; private set; }

  /// <summary>
  /// Maximum number of stamps that can be generated with a given base.
  /// </summary>
  public int MaxCalls { get; }

  /// <summary>
  /// Number of stamps remaining for this instance.
  /// </summary>
  public int RemainingCalls { get { return MaxCalls - CalledTimes; } }

  /// <summary>
  /// Instantiate a StampGenerator with a specific base.
  /// </summary>
  /// <param name="stampBase">Base of stamp.</param>
  /// <param name="calledTimes">
  /// Number of times this base has already been used.
  /// </param>
  public StampGenerator(string stampBase, int calledTimes = 0)
  {
    if (stampBase == null)
    {
      throw new ArgumentNullException("stampBase");
    }
    else if (Regex.IsMatch(stampBase, "[^a-zA-Z_0-9 \\-]"))
    {
      throw new ArgumentException("Invalid characters in Stamp Base.",
                                  "stampBase");
    }
    else if (stampBase.Length >= MAX_LENGTH_STAMP - 1)
    {
      throw new ArgumentException(
        string.Format("Stamp Base too long. (Length {0} out of {1})",
                      stampBase.Length, MAX_LENGTH_STAMP - 1), "stampBase");
    }
    else if (calledTimes < 0)
    {
      throw new ArgumentOutOfRangeException(
        "calledTimes", calledTimes, "calledTimes cannot be negative.");
    }
    else
    {
      int maxCalls = TRAILING_RANGE * (MAX_LENGTH_STAMP - stampBase.Length);
      if (calledTimes >= maxCalls)
      {
        throw new ArgumentOutOfRangeException(
          "calledTimes", calledTimes, string.Format(
            "Called Times too large; max for stem of length {0} is {1}.",
            stampBase.Length, maxCalls));
      }
      else
      {
        StampBase = stampBase;
        CalledTimes = calledTimes;
        MaxCalls = maxCalls;
      }
    }
  }

  /// <summary>
  /// Instantiate a StampGenerator with default base string based on time.
  /// </summary>
  public StampGenerator() : this(DateTime.Now.ToString("yMMddHHmm")) { }

  /// <summary>
  /// Generate a unique stamp.
  /// </summary>
  /// <remarks>
  /// Stamp values are orered like this:
  /// a, b, ... x, y, za, zb, ... zx, zy, zza, zzb, ...
  /// </remarks>
  /// <returns>A unique stamp.</returns>
  public override string ToString()
  {
    int zCount = CalledTimes / TRAILING_RANGE;
    int trailing = CalledTimes % TRAILING_RANGE;
    int length = StampBase.Length + zCount + 1;

    if (length > MAX_LENGTH_STAMP)
    {
      throw new InvalidOperationException(
        "Stamp length overflown! Cannot generate new stamps.");
    }
    else
    {
      CalledTimes = CalledTimes + 1;
      var builder = new StringBuilder(StampBase, length);
      builder.Append('z', zCount);
      builder.Append(_trailingChars[trailing]);
      return builder.ToString();
    }
  }
}

-1

DateTime.Now.Ticksist nicht sicher, Guid.NewGuid()ist zu hässlich, wenn Sie etwas sauberes und fast sicheres benötigen ( es ist nicht 100% sicher, wenn Sie es beispielsweise 1.000.000 Mal in 1 ms aufrufen ), versuchen Sie:

Math.Abs(Guid.NewGuid().GetHashCode())

Mit sicher meine ich sicher, einzigartig zu sein, wenn Sie es so oft in sehr kurzer Zeit in wenigen ms aufrufen .


Gibt es ein Problem mit meinem Lösungs-Downvoter? Lass es mich wissen, bitte.
Mehdi Dehghani

Die GetHashCodeMethode gibt ein zurück int, das einen 32-Bit-Bereich hat, während a GUIDeinen 128-Bit-Bereich hat und daher viel wahrscheinlicher eindeutig ist. Wenn Ihnen das Format eines GUIDWerts nicht gefällt , rufen ToString("N")Sie ihn einfach auf, wodurch die Bindestriche entfernt werden.
4thex
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.