唯一的随机字符串生成

Posted

技术标签:

【中文标题】唯一的随机字符串生成【英文标题】: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 你唯一的希望是让它变得如此随机和复杂,以至于对于所有实际目的,这些值都是唯一的(即碰撞概率极低)。 【参考方案1】:

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

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

【讨论】:

投了反对票,因为这个答案与 C# 无关。它可能会在不同的问题上找到更好的归宿。 那里几乎没有注意到 c# 标签。【参考方案2】:

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 已修复。谢谢!【参考方案3】:

我很惊讶为什么没有 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);

【讨论】:

【参考方案4】:

这很适合我

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

【讨论】:

【参考方案5】:

我简化了@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();
       

【讨论】:

【参考方案6】:

试试 Guid 和 Time.Ticks 的组合

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

【讨论】:

【参考方案7】:

如果您想要包含小写 大写字符 ([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)

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

使用 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 数据结构),我会邀请您喝杯啤酒。随意使用尽可能多的内核、线程、同步原语等。【参考方案10】:

我要提醒的是,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 教育很重要。)【参考方案11】:

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

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

【讨论】:

【参考方案12】:

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 中,检查它们是否存在,然后将它们添加到字符串中或相应地跳过它们。 【参考方案13】:

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

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

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

【讨论】:

正如@Keltex 指出的那样:WinAPI GUID 生成器的密码分析表明,由于 V4 GUID 的序列是伪随机的,因此在初始状态下,可以预测由函数 UuidCreate。【参考方案14】: 不确定微软的链接是随机生成的 看看 new Guid().ToString()

【讨论】:

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

以上是关于唯一的随机字符串生成的主要内容,如果未能解决你的问题,请参考以下文章

唯一的随机字符串生成

PHP生成随机字符串与唯一字符串

PHP生成随机或者唯一字符串

sql 生成唯一随机数字字符串

VBA 生成随机唯一的字母数字字符串

生成 900 万个唯一的随机数字字符串