【问题标题】:Unique random string generation唯一的随机字符串生成
【发布时间】:2010-10-18 07:59:35
【问题描述】:

我想生成随机的唯一字符串,例如由 MSDN 库生成的字符串。(Error Object)。应该生成一个像 't9zk6eay' 这样的字符串。

【问题讨论】:

  • 试试这个string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);更多可以找到here
  • 对于完全独特的东西,它必须基于非随机的东西,如时间、位置等,因此实际上永远不可能是完全随机的。 Guid 可能看起来是随机的,但实际上并非如此。 IMO 你唯一的希望是让它变得如此随机和复杂,以至于对于所有实际目的,这些值都是唯一的(即碰撞概率极低)。

标签: c# random


【解决方案1】:
  • 不确定微软的链接是随机生成的
  • 看看 new Guid().ToString()

【讨论】:

  • 你的意思是 Guid.NewGuid().ToString() - Guid 没有公共构造函数
  • 您可能是对的,输入时没有验证。我相信原始海报有重点。
【解决方案2】:

我不认为它们真的是随机的,但我猜那些是一些哈希值。

每当我需要一些随机标识符时,我通常会使用 GUID 并将其转换为它的“裸”表示:

Guid.NewGuid().ToString("n");

【讨论】:

  • 正如@Keltex 指出的那样:WinAPI GUID 生成器的密码分析表明,由于 V4 GUID 的序列是伪随机的,因此在初始状态下,可以预测由函数 UuidCreate.
【解决方案3】:

Get Unique Key using GUID Hash code

public static string GetUniqueKey(int length)
{
    string guidResult = string.Empty;

    while (guidResult.Length < length)
    {
        // Get the GUID.
        guidResult += Guid.NewGuid().ToString().GetHashCode().ToString("x");
    }

    // Make sure length is valid.
    if (length <= 0 || length > guidResult.Length)
        throw new ArgumentException("Length must be between 1 and " + guidResult.Length);

    // Return the first length bytes.
    return guidResult.Substring(0, length);
}

【讨论】:

  • 这很有效,但随机单词不包含唯一字符。字符是重复的,如 114e3(两个 1)、eaaea(三个 a 和两个 e)、60207(两个 0)等等。如何使用字母数字组合生成不重复字符的随机字符串?
  • @vijay:由于它输出的是十六进制数字,所以您将自己限制为 16 个字符,而 16 个!可能的输出。随机字符串就是这样,随机的。理论上你可以得到一串全 a (aaaaaaaaaaaaaa)。这是非常不可能的,但不比任何其他随机字符串更不可能。我不确定您为什么需要该约束,但是当您向字符串添加字符时,将它们弹出到 HashSet 中,检查它们是否存在,然后将它们添加到字符串中或相应地跳过它们。
【解决方案4】:

这已被要求提供各种语言。这是one question about passwords,它也应该适用于这里。

如果您想使用字符串来缩短 URL,您还需要字典 或数据库检查以查看生成的 ID 是否已被使用。

【讨论】:

    【解决方案5】:

    我要提醒的是,GUID 不是随机数。它们不应被用作生成任何您期望完全随机的东西的基础(请参阅http://en.wikipedia.org/wiki/Globally_Unique_Identifier):

    WinAPI GUID 生成器的密码分析表明,由于 V4 GUID 的序列是伪随机的,因此在初始状态下,可以预测函数 UuidCreate 返回的最多 250 000 个 GUID。这就是为什么不应在密码学中使用 GUID,例如。 g.,作为随机键。

    相反,只需使用 C# Random 方法。像这样的东西(code found here):

    private string RandomString(int size)
    {
      StringBuilder builder = new StringBuilder();
      Random random = new Random();
      char ch ;
      for(int i=0; i<size; i++)
      {
        ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))) ;
        builder.Append(ch);
      }
      return builder.ToString();
    }
    

    如果您想要 唯一 的东西(例如数据库中的唯一文件名或键),则 GUID 很好,但它们不适合您想要 随机 的东西(像密码或加密密钥)。所以这取决于你的应用程序。

    编辑。微软表示 Random 也不是很好 (http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx):

    例如,要生成适合创建随机密码的加密安全随机数,请使用派生自 System.Security.Cryptography.RandomNumberGenerator 的类,例如 System.Security.Cryptography.RNGCryptoServiceProvider。

    【讨论】:

    • C# 随机类也不是“随机的”,也不适合任何加密代码,因为它是一个从特定种子数开始的经典随机生成器。相同的种子也将返回相同的返回数字序列; GUID 方法在这里已经好很多了(不是“随机”而是“唯一”)。
    • @Lucero:你是对的。 Microsoft 建议,“例如,要生成适合创建随机密码的加密安全随机数,请使用派生自 System.Security.Cryptography.RandomNumberGenerator 的类,例如 System.Security.Cryptography.RNGCryptoServiceProvider。”
    • 好吧,问题已经表明他想要(伪)随机唯一字符串,因此没有加密要求,甚至不需要遵循特定的随机分布。所以 GUID 可能是最简单的方法。
    • 对于 any PRNG 来说,“给定初始状态,可以预测到下一个 250 000 个 GUID”的声明似乎是一个天生正确的声明......我相信它是也不安全,但我不确定生成真正随机的 URL 有多大价值,如果这就是 OP 的目的的话。 ;)
    • (无论如何+1 -- PRNG 教育很重要。)
    【解决方案6】:

    使用 Guid 将是一个很好的方法,但要获得类似于您的示例的内容,您可能需要将其转换为 Base64 字符串:

        Guid g = Guid.NewGuid();
        string GuidString = Convert.ToBase64String(g.ToByteArray());
        GuidString = GuidString.Replace("=","");
        GuidString = GuidString.Replace("+","");
    

    为了更接近您的示例,我去掉了“=”和“+”,否则您的字符串末尾会出现“==”,中间会出现“+”。这是一个示例输出字符串:

    “OZVV5TpP4U6wJthaCORZEQ”

    【讨论】:

    • 你也应该考虑替换 /。
    • 不应将 Guid 视为安全随机字符串,因为可以猜测序列。 Guid 旨在避免密钥冲突,而不是随机的。堆栈溢出时有一些好的discussions of the randomness of a Guid
    • 要清楚简短地解释 Convert.ToBase64String 的含义,take a look here
    • 可以将guid转换为base64并替换+和=增加碰撞概率吗?
    • @SimonEjsing 如果您真的可以编写一个应用程序,该应用程序在使用new Guid() 时不会发生“黑客攻击”(篡改时钟或内部 Windows 数据结构),我会邀请您喝杯啤酒。随意使用尽可能多的内核、线程、同步原语等。
    【解决方案7】:

    2016 年 1 月 23 日更新

    如果你觉得这个答案有用,你可能对a simple (~500 SLOC) password generation library I published感兴趣:

    Install-Package MlkPwgen
    

    然后你可以像下面的答案一样生成随机字符串:

    var str = PasswordGenerator.Generate(length: 10, allowed: Sets.Alphanumerics);
    

    该库的一个优点是代码可以更好地分解,因此您可以使用安全随机性for more than generating strings。查看the project site了解更多详情。

    原答案

    由于还没有人提供安全代码,因此我发布以下内容以防有人发现它有用。

    string RandomString(int length, string allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") {
        if (length < 0) throw new ArgumentOutOfRangeException("length", "length cannot be less than zero.");
        if (string.IsNullOrEmpty(allowedChars)) throw new ArgumentException("allowedChars may not be empty.");
    
        const int byteSize = 0x100;
        var allowedCharSet = new HashSet<char>(allowedChars).ToArray();
        if (byteSize < allowedCharSet.Length) throw new ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize));
    
        // Guid.NewGuid and System.Random are not particularly random. By using a
        // cryptographically-secure random number generator, the caller is always
        // protected, regardless of use.
        using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create()) {
            var result = new StringBuilder();
            var buf = new byte[128];
            while (result.Length < length) {
                rng.GetBytes(buf);
                for (var i = 0; i < buf.Length && result.Length < length; ++i) {
                    // Divide the byte into allowedCharSet-sized groups. If the
                    // random value falls into the last group and the last group is
                    // too small to choose from the entire allowedCharSet, ignore
                    // the value in order to avoid biasing the result.
                    var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);
                    if (outOfRangeStart <= buf[i]) continue;
                    result.Append(allowedCharSet[buf[i] % allowedCharSet.Length]);
                }
            }
            return result.ToString();
        }
    }
    

    感谢 Ahmad 指出如何让代码在 .NET Core 上运行。

    【讨论】:

    • @Keltex 解决方案不适用于 meh(它在几次使用后返回相同的字符串)。这个解决方案完美:)
    • @LeeGrissom,偏见是一个重要方面。例如,假设您的字母表包含 255 个字符,并且您得到一个介于 0-255 之间的随机值。在环形缓冲区中,值 0 和 255 都对应于相同的字符,这会使结果偏向于字母表中的第一个字符,它的随机性会降低。当然,这是否重要取决于应用程序。
    • 谁的目标是.netcore:将var rng = new RNGCryptoServiceProvider()替换为var rng = RandomNumberGenerator.Create()
    • 为什么要计算'var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);'每次迭代?您可以在“使用”之前计算它。
    • @BartCalixto 已修复。谢谢!
    【解决方案8】:

    我简化了@Michael Kropats 解决方案并制作了一个 LINQ 风格的版本。

    string RandomString(int length, string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    {       
        var outOfRange = byte.MaxValue + 1 - (byte.MaxValue + 1) % alphabet.Length;
    
        return string.Concat(
            Enumerable
                .Repeat(0, int.MaxValue)
                .Select(e => RandomByte())
                .Where(randomByte => randomByte < outOfRange)
                .Take(length)
                .Select(randomByte => alphabet[randomByte % alphabet.Length])
        );
    }
    
    byte RandomByte()
    {
        using (var randomizationProvider = new RNGCryptoServiceProvider())
        {
            var randomBytes = new byte[1];
            randomizationProvider.GetBytes(randomBytes);
            return randomBytes.Single();
        }   
    }
    

    【讨论】:

      【解决方案9】:

      VB.net 中的 Michael Kropats 解决方案

      Private Function RandomString(ByVal length As Integer, Optional ByVal allowedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") As String
          If length < 0 Then Throw New ArgumentOutOfRangeException("length", "length cannot be less than zero.")
          If String.IsNullOrEmpty(allowedChars) Then Throw New ArgumentException("allowedChars may not be empty.")
      
      
          Dim byteSize As Integer = 256
          Dim hash As HashSet(Of Char) = New HashSet(Of Char)(allowedChars)
          'Dim hash As HashSet(Of String) = New HashSet(Of String)(allowedChars)
          Dim allowedCharSet() = hash.ToArray
      
          If byteSize < allowedCharSet.Length Then Throw New ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize))
      
      
          ' Guid.NewGuid and System.Random are not particularly random. By using a
          ' cryptographically-secure random number generator, the caller is always
          ' protected, regardless of use.
          Dim rng = New System.Security.Cryptography.RNGCryptoServiceProvider()
          Dim result = New System.Text.StringBuilder()
          Dim buf = New Byte(128) {}
          While result.Length < length
              rng.GetBytes(buf)
              Dim i
              For i = 0 To buf.Length - 1 Step +1
                  If result.Length >= length Then Exit For
                  ' Divide the byte into allowedCharSet-sized groups. If the
                  ' random value falls into the last group and the last group is
                  ' too small to choose from the entire allowedCharSet, ignore
                  ' the value in order to avoid biasing the result.
                  Dim outOfRangeStart = byteSize - (byteSize Mod allowedCharSet.Length)
                  If outOfRangeStart <= buf(i) Then
                      Continue For
                  End If
                  result.Append(allowedCharSet(buf(i) Mod allowedCharSet.Length))
              Next
          End While
          Return result.ToString()
      End Function
      

      【讨论】:

        【解决方案10】:

        如果您想要包含小写 大写字符 ([a-zA-Z0-9]) 的字母数字字符串,您可以使用 Convert.ToBase64String() 获得快速而简单的解决方案。 p>

        至于唯一性,请查看birthday problem 来计算发生冲突的可能性(A)生成的字符串的长度和(B)生成的字符串的数量。

        Random random = new Random();
        
        int outputLength = 10;
        int byteLength = (int)Math.Ceiling(3f / 4f * outputLength); // Base64 uses 4 characters for every 3 bytes of data; so in random bytes we need only 3/4 of the desired length
        byte[] randomBytes = new byte[byteLength];
        string output;
        do
        {
            random.NextBytes(randomBytes); // Fill bytes with random data
            output = Convert.ToBase64String(randomBytes); // Convert to base64
            output = output.Substring(0, outputLength); // Truncate any superfluous characters and/or padding
        } while (output.Contains('/') || output.Contains('+')); // Repeat if we contain non-alphanumeric characters (~25% chance if length=10; ~50% chance if length=20; ~35% chance if length=32)
        

        【讨论】:

          【解决方案11】:

          试试 Guid 和 Time.Ticks 的组合

           var randomNumber = Convert.ToBase64String(Guid.NewGuid().ToByteArray()) + DateTime.Now.Ticks;
               randomNumber = System.Text.RegularExpressions.Regex.Replace(randomNumber, "[^0-9a-zA-Z]+", "");
          

          【讨论】:

            【解决方案12】:

            这很适合我

                private string GeneratePasswordResetToken()
                {
                    string token = Guid.NewGuid().ToString();
                    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(token);
                    return Convert.ToBase64String(plainTextBytes);
                }
            

            【讨论】:

              【解决方案13】:

              我很惊讶为什么没有 CrytpoGraphic 解决方案。 GUID 是唯一的,但不是加密安全的See this Dotnet Fiddle.

              var bytes = new byte[40]; // byte size
              using (var crypto = new RNGCryptoServiceProvider())
                crypto.GetBytes(bytes);
              
              var base64 = Convert.ToBase64String(bytes);
              Console.WriteLine(base64);
              

              如果您想在前面添加 Guid:

              var result = Guid.NewGuid().ToString("N") + base64;
              Console.WriteLine(result);
              

              更简洁的字母数字字符串:

              result = Regex.Replace(result,"[^A-Za-z0-9]","");
              Console.WriteLine(result);
              

              【讨论】:

                【解决方案14】:

                我在 Windows 上使用 Linux 命令的一站式解决方案是 scoop。 从scoop.sh安装scoop

                scoop install openssl
                openssl rand -base64 32
                Dca3c3pptVkcb8fx243wN/3f/rQxx/rWYL8y7rZrGrA=
                

                【讨论】:

                • 投了反对票,因为这个答案与 C# 无关。它可能会在不同的问题上找到更好的归宿。
                • 那里几乎没有注意到 c# 标签。
                猜你喜欢
                • 2012-11-21
                • 1970-01-01
                • 1970-01-01
                • 2011-08-18
                • 2017-02-21
                • 1970-01-01
                • 1970-01-01
                • 2014-09-09
                相关资源
                最近更新 更多