How to Generate unique file names in C#

后端 未结 19 2320
生来不讨喜
生来不讨喜 2020-12-02 05:43

I have implemented an algorithm that will generate unique names for files that will save on hard drive. I\'m appending DateTime: Hours,Minutes,Second an

相关标签:
19条回答
  • 2020-12-02 06:22

    I ends up concatenating GUID with Day Month Year Second Millisecond string and i think this solution is quite good in my scenario

    0 讨论(0)
  • 2020-12-02 06:22

    I wrote a class specifically for doing this. It's initialized with a "base" part (defaults to a minute-accurate timestamp) and after that appends letters to make unique names. So, if the first stamp generated is 1907101215a, the second would be 1907101215b, then 1907101215c, et cetera.

    If I need more than 25 unique stamps then I use unary 'z's to count 25's. So, it goes 1907101215y, 1907101215za, 1907101215zb, ... 1907101215zy, 1907101215zza, 1907101215zzb, and so forth. This guarantees that the stamps will always sort alphanumerically in the order they were generated (as long as the next character after the stamp isn't a letter).

    It isn't thread-safe, doesn't automatically update the time, and quickly bloats if you need hundreds of stamps, but I find it sufficient for my needs.

    /// <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();
        }
      }
    }
    
    0 讨论(0)
  • 2020-12-02 06:22

    DateTime.Now.Ticks is not safe, Guid.NewGuid() is too ugly, if you need something clean and almost safe (it's not 100% safe for example if you call it 1,000,000 times in 1ms), try:

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

    By safe I mean safe to be unique when you call it so many times in very short period few ms of time.

    0 讨论(0)
  • 2020-12-02 06:24

    Do you need the date time stamp in the filename?

    You could make the filename a GUID.

    0 讨论(0)
  • 2020-12-02 06:26

    If readability doesn't matter, use GUIDs.

    E.g.:

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

    or shorter:

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

    In my programs, I sometimes try e.g. 10 times to generate a readable name ("Image1.png"…"Image10.png") and if that fails (because the file already exists), I fall back to GUIDs.

    Update:

    Recently, I've also use DateTime.Now.Ticks instead of GUIDs:

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

    or

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

    The benefit to me is that this generates a shorter and "nicer looking" filename, compared to GUIDs.

    Please note that in some cases (e.g. when generating a lot of random names in a very short time), this might make non-unique values.

    Stick to GUIDs if you want to make really sure that the file names are unique, even when transfering them to other computers.

    0 讨论(0)
  • 2020-12-02 06:32

    you can use Random.Next() also to generate a random number. you can see the MSDN link: http://msdn.microsoft.com/en-us/library/9b3ta19y.aspx

    0 讨论(0)
提交回复
热议问题