独特的随机字符串生成


97

我想生成随机的唯一字符串,例如由MSDN库生成的字符串。(错误对象)。应该生成类似“ t9zk6eay”的字符串。


1
试试看string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);更多可以在这里
shaijut

1
对于完全唯一的事物,它必须基于非随机的事物,例如时间,位置等,因此永远不可能完全随机。Guid似乎是随机的,但实际上并非如此。海事组织,您唯一的希望是使它变得如此随机和复杂,以至于在所有实际用途中,值都是唯一的(即发生碰撞的可能性极低)。
bytedev

Answers:


84

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

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

我摆脱了“ =”和“ +”以便更接近您的示例,否则您将在字符串的末尾得到“ ==”,在字符串的中间得到“ +”。这是一个示例输出字符串:

“ OZVV5TpP4U6wJthaCORZEQ”


15
您也应该考虑更换/。
贾森·基利2009年

20
Guid不应被视为安全的随机字符串,因为可以猜测序列。Guid旨在避免键冲突,而不是随机发生。关于堆栈溢出的Guid的随机性有一些很好的讨论
丹尼尔·布拉德利2013年

对于什么明确和简短的解释Convert.ToBase64String是关于,看看这里
jwaliszko 2014年

2
可以将guid转换为base64并替换+和=增加碰撞概率吗?
米兰阿格沃尔2015年

5
@SimonEjsing如果您实际上可以编写一个使用时会发生冲突new Guid()而不会“被黑客入侵”(篡改时钟或内部Windows数据结构)的应用程序,我将邀请您喝啤酒。随意使用任意数量的内核,线程,同步原语等。
Lucero

175

更新2016/1/23

如果您认为此答案有用,那么您可能对我发布的简单(〜500 SLOC)密码生成库感兴趣

Install-Package MlkPwgen

然后,您可以像下面的答案中那样生成随机字符串:

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

该库的一个优点是可以更好地分解代码,因此您可以使用安全随机性而不是生成字符串。查看项目站点以获取更多详细信息。

原始答案

由于尚无人提供安全代码,因此我张贴以下内容,以防有人觉得有用。

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(几次使用后返回相同的字符串)。该解决方案非常有效:)
JoanComasFdz 2012年

2
@LeeGrissom,偏见是一个重要方面。例如,假设您的字母包含255个字符,并且您获得了0-255之间的随机值。在环形缓冲区中,值0和255都将对应于同一字符,这会使结果偏向于字母中的第一个字符,因此随机性会降低。是否重要取决于应用程序。
OskarSjöberg2015年

4
谁的定位.netcore:替换var rng = new RNGCryptoServiceProvider()var rng = RandomNumberGenerator.Create()
AMD公司

1
为什么计算'var outOfRangeStart = byteSize-(byteSize%allowedCharSet.Length);' 对于每次迭代?您可以在“使用”之前进行计算。
mtkachenko

1
@BartCalixto固定。谢谢!
Michael Kropat

38

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

对WinAPI GUID生成器的加密分析表明,由于V4 GUID的序列是伪随机的,因此在给定初始状态的情况下,可以预测由函数UuidCreate返回的下一个250 000 GUID。这就是为什么GUID不应该在加密中使用的原因,例如,作为随机密钥。

相反,只需使用C#Random方法。像这样的东西(在这里找到代码):

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很好,但是对于您想要随机化的东西(例如密码或加密密钥),GUID 不好。因此,这取决于您的应用程序。

编辑。微软表示,Random也没有那么出色(http://msdn.microsoft.com/zh-cn/library/system.random(VS.71).aspx):

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


5
C#随机类也不是“随机”的,也不适合任何密码,因为它是从特定种子编号开始的经典随机生成器。相同的种子也将返回相同的数字序列;GUID方法在这里已经好多了(不是“随机”而是“唯一”)。
Lucero

3
@Lucero:你是对的。Microsoft建议:“例如,要生成适合创建随机密码的加密安全随机数,请使用派生自System.Security.Cryptography.RandomNumberGenerator的类,例如System.Security.Cryptography.RNGCryptoServiceProvider。”
Keltex

好了,这个问题已经表明他想要(伪)随机唯一字符串,因此不需要密码,甚至不需要遵循特定的随机分布。因此,GUID可能是最简单的方法。
乔伊(Joey)

1
对于任何 PRNG ,“给定初始状态可以预测多达25万个GUID”的说法似乎是一种内在正确的说法...我确定它也不安全,但是我不确定生成该值是否有价值真正随机的URL(如果这就是OP的目标)。;)
ojrac

1
(无论如何+1-PRNG教育很重要。)
ojrac

13

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

11

我不认为它们确实是随机的,但是我猜这些是一些哈希值。

每当需要随机标识符时,通常都会使用GUID并将其转换为其“裸”表示形式:

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

正如@Keltex指出的那样:WinAPI GUID生成器的密码分析表明,由于V4 GUID的序列是伪随机的,因此在初始状态下,一个人最多可以预测函数UuidCreate返回的下一个25万个GUID。
JoanComasFdz 2012年

4

尝试结合Guid和Time.Ticks

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

3

我很惊讶为什么没有适当的CrytpoGraphic解决方案。GUID是唯一的,但不是加密安全的参见此Dotnet小提琴。

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

1

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

1

这对我来说很完美

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

0

已要求提供多种语言。这是关于密码一个问题,也应该在这里适用。

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


0

如果要使用小写字母字母数字字符串大写字母([a-zA-Z0-9]),则可以使用Convert.ToBase64String()作为快速简单的解决方案。

至于唯一性,请检查生日问题,以计算发生碰撞的可能性(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)


-1

使用GUID哈希码获取唯一密钥

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 2012年

@vijay:因为它输出的是十六进制数字,所以您将自己限制为16个字符和16个字符!可能的输出。随机字符串就是随机的。从理论上讲,您可以得到所有a的字符串(aaaaaaaaaaaaaaaaaaa)。这是非常不可能的,但仅此而已。我不确定为什么需要该约束,但是在向字符串中添加字符时,将其放入HashSet <T>中,检查它们的存在,然后将其添加到字符串中或相应地跳过它们。
克里斯·多格特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.