我如何在c#中生成一个随机的8个字符的字母数字字符串?


当前回答

问:为什么我要浪费时间使用Enumerable ?范围,而不是输入“ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789”?

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    public static void Main()
    {
        var randomCharacters = GetRandomCharacters(8, true);
        Console.WriteLine(new string(randomCharacters.ToArray()));
    }

    private static List<char> getAvailableRandomCharacters(bool includeLowerCase)
    {
        var integers = Enumerable.Empty<int>();
        integers = integers.Concat(Enumerable.Range('A', 26));
        integers = integers.Concat(Enumerable.Range('0', 10));

        if ( includeLowerCase )
            integers = integers.Concat(Enumerable.Range('a', 26));

        return integers.Select(i => (char)i).ToList();
    }

    public static IEnumerable<char> GetRandomCharacters(int count, bool includeLowerCase)
    {
        var characters = getAvailableRandomCharacters(includeLowerCase);
        var random = new Random();
        var result = Enumerable.Range(0, count)
            .Select(_ => characters[random.Next(characters.Count)]);

        return result;
    }
}

答案:魔术弦不好。有没有人注意到在我的字符串顶部没有“I”?我妈妈教我不要用魔法弦就是因为这个原因……

注意事项1:正如许多像@dtb这样的人所说,不要使用系统。随机的,如果你需要密码安全…

注2:这个答案不是最有效或最短的,但我想用空间把答案和问题分开。我回答这个问题的目的,更多的是为了警告人们不要使用神奇的字符串,而不是提供一个花哨的创新答案。

其他回答

Eric J.写的代码很潦草(很明显这是6年前写的……他今天可能不会写那个代码),甚至还有一些问题。

与目前提出的一些替代方案不同,这个方案在密码学上是合理的。

不真实的…在密码中有一个偏差(正如在注释中所写的那样),bcdefgh比其他的更有可能(a不是,因为通过GetNonZeroBytes,它不会生成值为0的字节,因此a的偏差由它平衡),所以它在密码学上并不可靠。

这应该可以纠正所有的问题。

public static string GetUniqueKey(int size = 6, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new RNGCryptoServiceProvider())
    {
        var data = new byte[size];

        // If chars.Length isn't a power of 2 then there is a bias if
        // we simply use the modulus operator. The first characters of
        // chars will be more probable than the last ones.

        // buffer used if we encounter an unusable random byte. We will
        // regenerate it in this buffer
        byte[] smallBuffer = null;

        // Maximum random number that can be used without introducing a
        // bias
        int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);

        crypto.GetBytes(data);

        var result = new char[size];

        for (int i = 0; i < size; i++)
        {
            byte v = data[i];

            while (v > maxRandom)
            {
                if (smallBuffer == null)
                {
                    smallBuffer = new byte[1];
                }

                crypto.GetBytes(smallBuffer);
                v = smallBuffer[0];
            }

            result[i] = chars[v % chars.Length];
        }

        return new string(result);
    }
}

另一种选择是使用Linq并将随机字符聚合到stringbuilder中。

var chars = "abcdefghijklmnopqrstuvwxyz123456789".ToArray();
string pw = Enumerable.Range(0, passwordLength)
                      .Aggregate(
                          new StringBuilder(),
                          (sb, n) => sb.Append((chars[random.Next(chars.Length)])),
                          sb => sb.ToString());

对于加密和非加密,有效地:

public static string GenerateRandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
    new Random().GenerateRandomString(length, charset);

public static string GenerateRandomString(this Random random, int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
    RandomString(random.NextBytes, length, charset.ToCharArray());

public static string GenerateRandomCryptoString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider())
        return crypto.GenerateRandomCryptoString(length, charset);
}

public static string GenerateRandomCryptoString(this RNGCryptoServiceProvider random, int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") => 
    RandomString(random.GetBytes, length, charset.ToCharArray());

private static string RandomString(Action<byte[]> fillRandomBuffer, int length, char[] charset)
{
    if (length < 0)
        throw new ArgumentOutOfRangeException(nameof(length), $"{nameof(length)} must be greater or equal to 0");
    if (charset is null)
        throw new ArgumentNullException(nameof(charset));
    if (charset.Length == 0)
        throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));

    var maxIdx = charset.Length;
    var chars = new char[length];
    var randomBuffer = new byte[length * 4];
    fillRandomBuffer(randomBuffer);

    for (var i = 0; i < length; i++)
        chars[i] = charset[BitConverter.ToUInt32(randomBuffer, i * 4) % maxIdx];

    return new string(chars);
}

使用生成器和LINQ。不是最快的选项(特别是因为它不会一次生成所有字节),但相当整洁和可扩展:

private static readonly Random _random = new Random();

public static string GenerateRandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
    new string(_random.GetGenerator().RandomChars(charset.ToCharArray()).Take(length).ToArray());

public static string GenerateRandomCryptoString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider())
        return new string(crypto.GetGenerator().RandomChars(charset.ToCharArray()).Take(length).ToArray());
}

public static IEnumerable<char> RandomChars(this Func<uint, IEnumerable<uint>> randomGenerator, char[] charset)
{
    if (charset is null)
        throw new ArgumentNullException(nameof(charset));
    if (charset.Length == 0)
        throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));

    return randomGenerator((uint)charset.Length).Select(r => charset[r]);
}

public static Func<uint, IEnumerable<uint>> GetGenerator(this Random random)
{
    if (random is null)
        throw new ArgumentNullException(nameof(random));

    return GeneratorFunc_Inner;

    IEnumerable<uint> GeneratorFunc_Inner(uint maxValue)
    {
        if (maxValue > int.MaxValue)
            throw new ArgumentOutOfRangeException(nameof(maxValue));

        return Generator_Inner();

        IEnumerable<uint> Generator_Inner()
        {
            var randomBytes = new byte[4];
            while (true)
            {
                random.NextBytes(randomBytes);
                yield return BitConverter.ToUInt32(randomBytes, 0) % maxValue;
            }
        }
    }
}

public static Func<uint, IEnumerable<uint>> GetGenerator(this System.Security.Cryptography.RNGCryptoServiceProvider random)
{
    if (random is null)
        throw new ArgumentNullException(nameof(random));

    return Generator_Inner;

    IEnumerable<uint> Generator_Inner(uint maxValue)
    {
        var randomBytes = new byte[4];
        while (true)
        {
            random.GetBytes(randomBytes);
            yield return BitConverter.ToUInt32(randomBytes, 0) % maxValue;
        }
    }
}

一个更简单的版本,使用LINQ只用于非加密字符串:

private static readonly Random _random = new Random();

public static string RandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
    new string(_random.GenerateChars(charset).Take(length).ToArray()); 

public static IEnumerable<char> GenerateChars(this Random random, string charset)
{
    if (charset is null) throw new ArgumentNullException(nameof(charset));
    if (charset.Length == 0) throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));

    return random.Generator(charset.Length).Select(r => charset[r]);
}

public static IEnumerable<int> Generator(this Random random, int maxValue)
{
    if (random is null) throw new ArgumentNullException(nameof(random));

    return Generator_Inner();

    IEnumerable<int> Generator_Inner() { while (true) yield return random.Next(maxValue); }
}

不使用Random的解决方案:

var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);

var randomStr = new string(chars.SelectMany(str => str)
                                .OrderBy(c => Guid.NewGuid())
                                .Take(8).ToArray());

如果你的值不是完全随机的,但实际上可能依赖于某些东西——你可以计算出“某个东西”的md5或sha1哈希,然后将其截断为你想要的任何长度。

你也可以生成和截断一个guid。