如何生成随机的字母数字字符串?

1303

我该如何在C#中生成一个包含8个随机字母和数字的字符串?


2
你对字符集有什么限制吗?只能使用英文字符和0-9数字吗?可以混合大小写吗? - Eric J.
8
也许是前往https://dev59.com/MnRB5IYBdhLWcg3wET1J或https://dev59.com/C3NA5IYBdhLWcg3wBpBm。 - Jonas Elfström
9
请注意,生成密码时不应使用基于“Random”类的任何方法。因为“Random”的种子熵非常低,所以它并不真正安全。对于密码,应该使用加密伪随机数生成器(PRNG)。 - CodesInChaos
2
最好在这个问题中包含语言本地化。特别是如果你的GUI需要为中文或保加利亚语提供支持! - Peter Jamsmenson
22
这么多赞和高质量回答的内容不应该被标记为关闭。我投票支持重新开放它。 - John Coleman
显示剩余4条评论
38个回答

4
我正在寻找一个更具体的答案,我想控制随机字符串的格式,并偶然发现了这篇帖子。 例如:车牌(按国家)有特定的格式,我想创建随机车牌。
因此,我决定编写自己的Random扩展方法。(这是为了重复使用相同的Random对象,因为在多线程情况下可能会有重复对象)。
我创建了一个gist(https://gist.github.com/SamVanhoutte/808845ca78b9c041e928),但也将在此处复制扩展类:
void Main()
{
    Random rnd = new Random();
    rnd.GetString("1-###-000").Dump();
}

public static class RandomExtensions
{
    public static string GetString(this Random random, string format)
    {
        // Based on https://dev59.com/qHM_5IYBdhLWcg3whzjx
        // Added logic to specify the format of the random string (# will be random string, 0 will be random numeric, other characters remain)
        StringBuilder result = new StringBuilder();
        for(int formatIndex = 0; formatIndex < format.Length ; formatIndex++)
        {
            switch(format.ToUpper()[formatIndex])
            {
                case '0': result.Append(getRandomNumeric(random)); break;
                case '#': result.Append(getRandomCharacter(random)); break;
                default : result.Append(format[formatIndex]); break;
            }
        }
        return result.ToString();
    }

    private static char getRandomCharacter(Random random)
    {
        string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        return chars[random.Next(chars.Length)];
    }

    private static char getRandomNumeric(Random random)
    {
        string nums = "0123456789";
        return nums[random.Next(nums.Length)];
    }
}

4
现在以一行代码的方式呈现。
private string RandomName()
{
        return new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    using (var cryptoProvider = new RNGCryptoServiceProvider())
                        cryptoProvider.GetBytes(cryptoResult);

                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray());
}

3
每次访问都会改变的属性用于其他用途是相当可疑的。我建议使用方法代替。 - CodesInChaos
2
应该在使用后处理 RNGCryptoServiceProvider - Tsahi Asher
我解决了IDisposable问题,但是这仍然非常可疑,为每个字母创建一个新的RNGCryptoServiceProvider。 - piojo

4
一个包含所有字母和数字的解决方案,你可以随意更改:
public static string RandomString(int length)
{
    Random rand = new Random();
    string charbase = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    return new string(Enumerable.Range(0,length)
           .Select(_ => charbase[rand.Next(charbase.Length)])
           .ToArray());
}

如果你喜欢单行方法 ;)
public static Random rand = new Random();
public const string charbase = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 

public static string RandomString(int length) =>
        new string(Enumerable.Range(0,length).Select(_ => charbase[rand.Next(charbase.Length)]).ToArray());

4

对于加密和非加密的技术,都需要高效实现:

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

3

我知道很糟糕,但我就是忍不住:


namespace ConsoleApplication2
{
    using System;
    using System.Text.RegularExpressions;
class Program { static void Main(string[] args) { Random adomRng = new Random(); string rndString = string.Empty; char c;
for (int i = 0; i < 8; i++) { while (!Regex.IsMatch((c=Convert.ToChar(adomRng.Next(48,128))).ToString(), "[A-Za-z0-9]")); rndString += c; }
Console.WriteLine(rndString + Environment.NewLine); } } }

说明:这段代码是一个C#程序,它生成一个长度为8的随机字符串,只包含数字和字母。

2
这是Eric J的一种解决方案的变体,即针对WinRT(Windows Store应用程序)的加密安全方案:
public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new StringBuilder(length);
    for (int i = 0; i < length; ++i)
    {
        result.Append(CryptographicBuffer.GenerateRandomNumber() % chars.Length);
    }
    return result.ToString();
}

如果性能很重要(尤其是在长度比较长的情况下):
public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new System.Text.StringBuilder(length);
    var bytes = CryptographicBuffer.GenerateRandom((uint)length * 4).ToArray();
    for (int i = 0; i < bytes.Length; i += 4)
    {
        result.Append(BitConverter.ToUInt32(bytes, i) % chars.Length);
    }
    return result.ToString();
}

1
这并不是加密学上的做法。由于模运算没有将ulong的整个宽度均匀地分配到62个字符中,因此存在一定的偏差。 - Lie Ryan

2

我知道这不是最好的方法,但你可以尝试这个。

string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);

2
这一行代码是怎么样的?Console.WriteLine($"Random string: {Path.GetRandomFileName().Replace(".","")}"); 是一行代码。 - PmanAce

2

一种不使用 Random 的解决方案:

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

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

3
NewGuid 在内部使用随机数。因此,它仍然使用随机数,只是将其隐藏起来了。 - Wedge

1
我不知道这个加密方法的安全性如何,但它比其他复杂的解决方案更易读和简洁(在我看来),而且应该比基于System.Random的解决方案更“随机”。
return alphabet
    .OrderBy(c => Guid.NewGuid())
    .Take(strLength)
    .Aggregate(
        new StringBuilder(),
        (builder, c) => builder.Append(c))
    .ToString();

我无法决定我认为这个版本还是下一个版本更“漂亮”,但它们都会得到完全相同的结果:

return new string(alphabet
    .OrderBy(o => Guid.NewGuid())
    .Take(strLength)
    .ToArray());

虽然它不能为速度进行优化,因此如果每秒需要生成数百万个随机字符串,则请尝试其他方法!

注意:此解决方案不允许在字母表中重复使用符号,并且字母表必须等于或大于输出字符串的大小,这使得在某些情况下该方法不太理想,这完全取决于您的用例。


1
如果您的值不是完全随机的,而实际上可能取决于某些因素 - 您可以计算该“因素”的md5或sha1哈希值,然后将其截断为所需的任何长度。
此外,您还可以生成并截断GUID。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接