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

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个回答

2160

我听说LINQ是新黑,所以这是我尝试使用LINQ:

private static Random random = new Random();

public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return new string(Enumerable.Repeat(chars, length)
        .Select(s => s[random.Next(s.Length)]).ToArray());
}

(注意:使用 Random 类并不适合用于任何安全相关的事情,例如创建密码或令牌。如果需要一个强大的随机数生成器,请使用 RNGCryptoServiceProvider 类。)


32
@Alex: 我进行了几次快速测试,当生成更长的字符串时(只要有足够的内存可用),它似乎几乎呈线性扩展。话虽如此,在每个测试中,Dan Rigby的答案几乎比这个答案快两倍。 - LukeH
9
好的。如果你的标准是它使用linq,并且它的代码叙述很糟糕,那么它绝对是最好的选择。实际执行路径和代码叙述都相当低效和间接。别误会,我是个非常喜欢编程的人(我爱python),但这几乎就像一台鲁伯·戈尔德伯格机器。 - eremzeit
7
虽然这个回答从技术上回答了问题,但输出结果非常误导人。生成8个随机字符听起来可能有非常多的结果,但实际上最多只能产生20亿个不同的结果。而在实践中,结果更少。您还应该添加一个明确的警告,不要将此用于与安全相关的事情。 - CodesInChaos
57
小写字母留给读者自己练习。 - dtb
28
下面这行代码比给定的代码更加节省内存(因此也更快):return new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray()); - Tyson Williams
显示剩余11条评论

497
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();

for (int i = 0; i < stringChars.Length; i++)
{
    stringChars[i] = chars[random.Next(chars.Length)];
}

var finalString = new String(stringChars);

不如 Linq 解决方案优雅。

(注意: 使用 Random 类不适合用于任何安全相关的操作,例如创建密码或令牌。如果需要强随机数生成器,请使用 RNGCryptoServiceProvider 类。)


5
@Alex:这不是绝对最快的答案,但它是最快的“真实”答案(即允许控制使用的字符和字符串长度的答案)。 - LukeH
2
@Alex:Adam Porad的GetRandomFileName解决方案更快,但不允许控制使用的字符,并且最大可能长度为11个字符。Douglas的Guid解决方案非常快,但字符受限于A-F0-9,最大可能长度为32个字符。 - LukeH
1
@Adam:是的,你可以连接多次调用GetRandomFileName的结果,但这样做会(a)失去性能优势,(b)使你的代码变得更加复杂。 - LukeH
4
在循环之外创建Random()对象的实例。如果在短时间内创建大量Random()实例,那么调用.Next()将返回相同的值,因为Random()使用基于时间的种子。 - Dan Rigby
2
@xaisoft 不要将此答案用于任何安全关键的事情,例如密码。System.Random 不适合用于安全。 - CodesInChaos
显示剩余14条评论

449

更新至 .NET 6。RNGCryptoServiceProvider 已被标记为过时。现在应该调用 RandomNumberGenerator.Create()。答案中的代码已做相应更新。

基于评论进行更新。原始实现生成 a-h 的概率约为 1.95%,其余字符的概率约为 1.56%。更新后,所有字符的概率均约为 1.61%。

框架支持 - .NET Core 3(以及支持 .NET Standard 2.1 或以上版本的未来平台)提供了一个加密安全的方法 RandomNumberGenerator.GetInt32() 来生成所需范围内的随机整数。

与其他一些方案不同,这个方案是加密安全的

using System;
using System.Security.Cryptography;
using System.Text;

namespace UniqueKey
{
    public class KeyGenerator
    {
        internal static readonly char[] chars =
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); 

        public static string GetUniqueKey(int size)
        {            
            byte[] data = new byte[4*size];
            using (var crypto = RandomNumberGenerator.Create())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            for (int i = 0; i < size; i++)
            {
                var rnd = BitConverter.ToUInt32(data, i * 4);
                var idx = rnd % chars.Length;

                result.Append(chars[idx]);
            }

            return result.ToString();
        }

        public static string GetUniqueKeyOriginal_BIASED(int size)
        {
            char[] chars =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            byte[] data = new byte[size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            foreach (byte b in data)
            {
                result.Append(chars[b % (chars.Length)]);
            }
            return result.ToString();
        }
    }
}

根据这里的备选方案讨论并根据下面的评论进行更新/修改。

这是一个小的测试工具,演示了旧输出和更新输出中字符的分布情况。有关随机性分析的深入讨论,请查看random.org。

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

namespace CryptoRNGDemo
{
    class Program
    {

        const int REPETITIONS = 1000000;
        const int KEY_SIZE = 32;

        static void Main(string[] args)
        {
            Console.WriteLine("Original BIASED implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKeyOriginal_BIASED);

            Console.WriteLine("Updated implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKey);
            Console.ReadKey();
        }

        static void PerformTest(int repetitions, int keySize, Func<int, string> generator)
        {
            Dictionary<char, int> counts = new Dictionary<char, int>();
            foreach (var ch in UniqueKey.KeyGenerator.chars) counts.Add(ch, 0);

            for (int i = 0; i < REPETITIONS; i++)
            {
                var key = generator(KEY_SIZE); 
                foreach (var ch in key) counts[ch]++;
            }

            int totalChars = counts.Values.Sum();
            foreach (var ch in UniqueKey.KeyGenerator.chars)
            {
                Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
            }
        }
    }
}

更新于2022年7月25日

根据评论中的一个问题,我想知道这个分布是否真的是随机的。

我不是统计学家,但我可能可以在电视上扮演一个。如果有实际的统计学家想发表意见,那将是非常欢迎的。

有62个可能的输出值(A-Za-Z0-9)和int.MaxValue个数字用于选择一个数组索引。int.MaxValue % 62等于1,因此有一个字符会比其他字符被选中多大约四十亿倍。我们可以在索引之前通过随机旋转输出值的数组来进一步减少选择偏差。

T检验或其他统计量将是确定输出结果是否存在偏差的最佳方法,但这并不是我午休时间内能完成的事情。因此,我给你们留下了以上代码的修改版本,它可以衡量期望值的偏差。请注意,它趋向于零。

using System.Security.Cryptography;
using System.Text;

const int REPETITIONS = 1_000_000;
const int KEY_SIZE = 32;
int TASK_COUNT = Environment.ProcessorCount - 1;

var expectedPercentage = 100.0 / KeyGenerator.chars.Length;

var done = false;
var iterationNr = 1;
var totalRandomSymbols = 0L;

var grandTotalCounts = new Dictionary<char, long>();
foreach (var ch in KeyGenerator.chars) grandTotalCounts.Add(ch, 0);

while (!done)
{
    var experiments = Enumerable.Range(0, TASK_COUNT).Select(i => Task.Run(Experiment)).ToArray();
    Task.WaitAll(experiments);
    var totalCountsThisRun = experiments.SelectMany(e => e.Result)
        .GroupBy(e => e.Key)
        .Select(e => new { e.Key, Count = e.Select(_ => _.Value).Sum() })
        .ToDictionary(e => e.Key, e => e.Count);

    foreach (var ch in KeyGenerator.chars)
        grandTotalCounts[ch] += totalCountsThisRun[ch];

    var totalChars = grandTotalCounts.Values.Sum();
    totalRandomSymbols += totalChars;

    var distributionScores = KeyGenerator.chars.Select(ch =>
    new
    {
        Symbol = ch,
        OverUnder = (100.0 * grandTotalCounts[ch] / totalChars) - expectedPercentage

    });

    Console.WriteLine($"Iteration {iterationNr++}. Total random symbols: {totalRandomSymbols:N0}");
    foreach (var chWithValue in distributionScores.OrderByDescending(c => c.OverUnder))
    {
        Console.WriteLine($"{chWithValue.Symbol}: {chWithValue.OverUnder:#.00000}%");
    }

    done = Console.KeyAvailable;        
}

Dictionary<char, long> Experiment()
{
    var counts = new Dictionary<char, long>();
    foreach (var ch in KeyGenerator.chars) counts.Add(ch, 0);

    for (int i = 0; i < REPETITIONS; i++)
    {
        var key = KeyGenerator.GetUniqueKey(KEY_SIZE);
        foreach (var ch in key) counts[ch]++;
    }

    return counts;
}

public class KeyGenerator
{
    internal static readonly char[] chars =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();

    public static string GetUniqueKey(int size)
    {
        byte[] data = new byte[4 * size];
        using (var crypto = RandomNumberGenerator.Create())
        {
            crypto.GetBytes(data);
        }
        StringBuilder result = new StringBuilder(size);
        for (int i = 0; i < size; i++)
        {
            var rnd = BitConverter.ToUInt32(data, i * 4);
            var idx = rnd % chars.Length;

            result.Append(chars[idx]);
        }

        return result.ToString();
    }
}

12
在我看来,这似乎是正确的方法 - 不应该使用 Random() 生成随机密码、盐、熵等内容,因为它被优化用于速度且会生成可重复的数字序列;而另一方面,RNGCryptoServiceProvider.GetNonZeroBytes() 会产生不可预测的数字序列,因此更适合生成这些内容。 - mindplay.dk
27
这些字母略微有些偏差(255%62!=0)。尽管存在这个小缺陷,但它是目前为止最好的解决方案。 - CodesInChaos
14
请注意,如果您需要具有加密强度和无偏随机性,则此方法安全。 (如果您不需要这些功能,那么为什么要首选“RNGCSP”?)使用取模运算索引“chars”数组意味着除非“chars.Length”是256的约数,否则输出将存在偏差。 - LukeH
15
为了减少偏差的可能性,可以请求“4 * maxSize”随机字节,然后使用“(UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length”的结果。我还会使用“GetBytes”而不是“GetNonZeroBytes”。最后,您可以删除对“GetNonZeroBytes”的第一个调用,因为您没有使用它的结果。 - CodesInChaos
16
有趣的事实:A-Z、a-z和0-9共有62个字符。人们指出字母偏差,因为256 % 62 != 0。YouTube的视频ID包含A-Z、a-z、0-9以及"-"和"_",这产生了64种可能的字符,可以均匀地分成256。巧合吗?我认为不是! :) - qJake
显示剩余33条评论

234

解决方案1 - 最大范围,长度最灵活

string get_unique_string(int string_length) {
    using(var rng = new RNGCryptoServiceProvider()) {
        var bit_count = (string_length * 6);
        var byte_count = ((bit_count + 7) / 8); // rounded up
        var bytes = new byte[byte_count];
        rng.GetBytes(bytes);
        return Convert.ToBase64String(bytes);
    }
}

与使用GUID相比,此解决方案具有更大的范围,因为GUID有一些固定位始终相同,因此不是随机的,例如十六进制中的第13个字符始终为“4”- 至少在版本6 GUID中。

此解决方案还允许您生成任意长度的字符串。

解决方案2- 一行代码-适用于最多22个字符

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);

如果使用解决方案1生成的字符串与GUID中固定位导致范围不同,那么你将无法生成同样长的字符串,但在许多情况下,这种方法将能够胜任。

解决方案3 - 代码稍微少一些

Guid.NewGuid().ToString("n").Substring(0, 8);

这里大部分是为了历史记录而保留。它使用的代码稍微少一些,但是换来的代价是范围更小 - 因为它使用十六进制而不是base64,所以表示相同范围需要更多的字符,与其他解决方案相比。

这意味着发生冲突的机会更多 - 对生成的8个字符字符串进行100,000次迭代测试,有一个重复。


22
你实际上生成了一个重复的吗?考虑到GUID可能的5316911983139663491615228241121400000种组合,这令人惊讶。 - Alex
73
@Alex:他把GUID缩短为8个字符,这样碰撞的概率比GUID要高得多。 - dtb
25
除了极客,没有人能够欣赏这个 :) 是的,你说得完全正确,字符限制为8会有所不同。 - Alex
31
Guid.NewGuid().ToString("n")可以保留GUID中的数字和字母,无需使用Replace()函数。但需要注意的是,GUID仅由0-9和A-F组成。它们的组合数量“足够好”,但与真正的字母数字随机字符串相比还有差距。碰撞的概率为1:4,294,967,296 -- 与随机32位整数相同。 - richardtallent
34
  1. GUID是设计为唯一的,而不是随机的。尽管当前的Windows版本生成的是确实随机的V4 GUID,但不能保证如此。例如,旧版本的Windows使用V1 GUID,这可能导致您的代码失败。
  2. 仅使用十六进制字符会显著降低随机字符串的质量,从47位降至32位。
  3. 人们低估了碰撞概率,因为他们只考虑了两个值之间的碰撞概率。如果您生成了100K个32位值,则它们之间可能会发生一次碰撞。请参考生日悖论。
- CodesInChaos
显示剩余5条评论

99

这里有一个例子,我从Sam Allen在Dot Net Perls的例子中偷来的。

如果你只需要8个字符,那么可以使用System.IO命名空间中的Path.GetRandomFileName()方法。Sam说在这里使用"Path.GetRandomFileName"方法有时更好,因为它使用RNGCryptoServiceProvider获得更好的随机性。然而,它只限于11个随机字符。

GetRandomFileName总是返回一个12个字符的字符串,在第9个字符处带有一个句点。因此,你需要去掉句点(因为那不是随机的),然后从字符串中取8个字符。实际上,你只需取前8个字符而不用担心句点。

public string Get8CharacterRandomString()
{
    string path = Path.GetRandomFileName();
    path = path.Replace(".", ""); // Remove period.
    return path.Substring(0, 8);  // Return 8 character string
}

提示:感谢Sam


40
这个很有效。我运行了10万次迭代,从未出现过重复的名称。然而,我确实发现了几个粗俗词汇(用英语表示)。如果您要将其用于用户可见的内容,请注意一下。其中列表中的第一个单词就包含了“F***”。 - techturtle
4
谢谢你的提醒,我想使用包含字母表中所有字母的任意字符串生成器都存在使用粗俗语言的风险。我会注意的。 - Adam Porad
简单易懂,但不适用于长字符串...支持这个好技巧。 - Maher Abuthraa
8
有时会出现粗俗的言辞,但如果您将其持续运行足够长的时间,最终会写出莎士比亚的作品。(仅需要几个宇宙生命的寿命。:) - Slothario
@crush 你说得对。如果只需要8个或更少的字符,则不需要删除句号/“。”字符。我在答案的最后一句中提到了这一点。只是以防有人想使用答案生成超过8个字符,我想确保他们知道需要删除句号。 - Adam Porad
显示剩余4条评论

43

我代码的主要目标是:

  1. 字符串的分布几乎是均匀的(不关心小偏差,只要它们很小)。
  2. 为每个参数集输出超过数十亿个字符串。如果你的伪随机数生成器(PRNG)仅能生成20亿个(31位熵)不同的值,则生成一个8个字符的字符串(约47位熵)毫无意义。
  3. 它是安全的,因为我希望人们用它来生成密码或其他安全令牌。

第一个属性通过将64位值模除字母表大小来实现。对于小字母表(例如问题中的62个字符),这会导致可忽略的偏差。第二和第三个属性是通过使用RNGCryptoServiceProvider而不是System.Random来实现的。

using System;
using System.Security.Cryptography;

public static string GetRandomAlphanumericString(int length)
{
    const string alphanumericCharacters =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        "abcdefghijklmnopqrstuvwxyz" +
        "0123456789";
    return GetRandomString(length, alphanumericCharacters);
}

public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
    if (length < 0)
        throw new ArgumentException("length must not be negative", "length");
    if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
        throw new ArgumentException("length is too big", "length");
    if (characterSet == null)
        throw new ArgumentNullException("characterSet");
    var characterArray = characterSet.Distinct().ToArray();
    if (characterArray.Length == 0)
        throw new ArgumentException("characterSet must not be empty", "characterSet");

    var bytes = new byte[length * 8];
    var result = new char[length];
    using (var cryptoProvider = new RNGCryptoServiceProvider())
    {
        cryptoProvider.GetBytes(bytes);
    }
    for (int i = 0; i < length; i++)
    {
        ulong value = BitConverter.ToUInt64(bytes, i * 8);
        result[i] = characterArray[value % (uint)characterArray.Length];
    }
    return new string(result);
}

1
64 x Z 和 Math.Pow(2,Y) 之间没有交集。因此,尽管增加数字可以减少偏差,但不能消除它。我在下面更新了我的答案,我的方法是放弃随机输入并替换为另一个值。 - Kind Contributor
@Todd 我知道这并不能消除偏见,但我选择了这个简单的解决方案,而不是消除一个几乎无关紧要的偏见。 - CodesInChaos
我同意在大多数情况下,这可能实际上是不相关的。但现在我已经更新了我的代码,使其既像随机函数一样快速,又比你的代码更安全一些。所有代码都是开源的,供所有人分享。是的,我在这上面浪费了太多时间... - Kind Contributor
如果我们使用RNG提供程序,理论上有没有避免偏差的方法?我不确定...如果Todd的意思是在他生成额外的随机数时(当我们处于偏差区域时),那么这可能是错误的假设。RNG几乎具有所有生成值的线性分布。但这并不意味着我们不会在生成的字节之间有局部相关性。因此,仅针对偏差区域的附加字节仍然可能给我们带来一些偏差,但由于不同的原因,最可能这种偏差非常小。但在这种情况下,增加总生成字节数是更直接的方法。 - Maxim
1
@Maxim 你可以使用拒绝采样来完全消除偏差(假设底层生成器是完美随机的)。作为交换,代码可能会任意运行很长时间(概率指数级小)。 - CodesInChaos
显示剩余2条评论

38

最简单的:

public static string GetRandomAlphaNumeric()
{
    return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}

如果您硬编码字符数组并依赖于 System.Random, 您可以获得更好的性能:

public static string GetRandomAlphaNumeric()
{
    var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}
如果你担心英文字母可能会在某个时候改变,导致你失去业务,那么你可以避免使用硬编码,但是性能会稍微差一些(与Path.GetRandomFileName方法相当)。

如果您担心英文字母可能会出现问题,导致您失去业务,那么您可以避免硬编码,但应该表现略差(与Path.GetRandomFileName方法相当)。

public static string GetRandomAlphaNumeric()
{
    var chars = 'a'.To('z').Concat('0'.To('9')).ToList();
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

public static IEnumerable<char> To(this char start, char end)
{
    if (end < start)
        throw new ArgumentOutOfRangeException("the end char should not be less than start char", innerException: null);
    return Enumerable.Range(start, end - start + 1).Select(i => (char)i);
}

如果您可以将这两种方法作为System.Random实例的扩展方法,那么它们看起来更好。


1
使用 chars.Select 很丑陋,因为它依赖于输出大小最多为字母表大小。 - CodesInChaos
@CodesInChaos,我不确定我是否理解你的意思。你是指在“'a'.To('z')”方法中吗? - nawfal
1
  1. 只有当 chars.Count >= n 时,chars.Select().Take(n)才能正常工作。在一个你实际上不使用的序列上进行选择有点不直观,特别是带有隐式长度约束。我更愿意使用Enumerable.RangeEnumerable.Repeat`。
  2. 错误信息“结束字符应小于起始字符”是错误的/缺少了一个“不”。
- CodesInChaos
@CodesInChaos但是在我的情况下 'chars.Count' 远远大于'n'。而且我不明白那个不直观的部分。这难道不会使所有使用'Take'变得不直观吗?我不相信。感谢指出错别字。 - nawfal
5
这篇文章被放在theDailyWTF.com上作为CodeSOD文章。 - user177800
我只需要它用于单元测试。这太完美了! - CindyH

24

这篇帖子中的各种答案进行了一些性能比较:

方法和设置

// what's available
public static string possibleChars = "abcdefghijklmnopqrstuvwxyz";
// optimized (?) what's available
public static char[] possibleCharsArray = possibleChars.ToCharArray();
// optimized (precalculated) count
public static int possibleCharsAvailable = possibleChars.Length;
// shared randomization thingy
public static Random random = new Random();


// https://dev59.com/qHM_5IYBdhLWcg3whzjx#1344242
public string LinqIsTheNewBlack(int num) {
    return new string(
    Enumerable.Repeat(possibleCharsArray, num)
              .Select(s => s[random.Next(s.Length)])
              .ToArray());
}

// https://dev59.com/qHM_5IYBdhLWcg3whzjx#1344258
public string ForLoop(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleCharsArray[random.Next(possibleCharsAvailable)];
    }
    return new string(result);
}

public string ForLoopNonOptimized(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleChars[random.Next(possibleChars.Length)];
    }
    return new string(result);
}

public string Repeat(int num) {
    return new string(new char[num].Select(o => possibleCharsArray[random.Next(possibleCharsAvailable)]).ToArray());
}

// https://dev59.com/qHM_5IYBdhLWcg3whzjx#1518495
public string GenerateRandomString(int num) {
  var rBytes = new byte[num];
  random.NextBytes(rBytes);
  var rName = new char[num];
  while(num-- > 0)
    rName[num] = possibleCharsArray[rBytes[num] % possibleCharsAvailable];
  return new string(rName);
}

//SecureFastRandom - or SolidSwiftRandom
static string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; 
    char[] rName = new char[Length];
    SolidSwiftRandom.GetNextBytesWithMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

结果

在 LinqPad 中进行测试,对于字符串长度为 10,生成以下结果:

  • Linq 方法 = chdgmevhcy [10]
  • for 循环方法 = gtnoaryhxr [10]
  • Select 方法 = rsndbztyby [10]
  • 随机字符串生成方法 = owyefjjakj [10]
  • 安全快速随机数生成方法 = VzougLYHYP [10]
  • 无缓存的安全快速随机数生成方法 = oVQXNGmO1S [10]

性能指标可能会有轻微差异,很少情况下非优化方法实际上更快,有时候 for 循环和随机字符串生成方法会互相交替领先。

  • Linq 方法 (10000 次) = 96762 个节拍已过去 (9.6762 毫秒)
  • for 循环方法 (10000 次) = 28970 个节拍已过去 (2.897 毫秒)
  • 非优化的 for 循环方法 (10000 次) = 33336 个节拍已过去 (3.3336 毫秒)
  • Repeat 方法 (10000 次) = 78547 个节拍已过去 (7.8547 毫秒)
  • 随机字符串生成方法 (10000 次) = 27416 个节拍已过去 (2.7416 毫秒)
  • 安全快速随机数生成方法 (10000 次) = 13176 个节拍已过去 (5 毫秒) 最低 [不同的机器]
  • 无缓存的安全快速随机数生成方法 (10000 次) = 39541 个节拍已过去 (17 毫秒) 最低 [不同的机器]

4
了解哪些创建了重复项会很有趣。 - Rebecca
@Junto -- 要找出重复的结果,可以使用以下类似的代码: var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());,其中你需要将 EachRandomizingMethod 替换为每个方法。 - drzaus

21

1
看起来微软移动了链接..另一个代码示例在https://msdn.microsoft.com/en-us/library/ms152017或http://aspnet.4guysfromrolla.com/demos/GeneratePassword.aspx或http://developer.xamarin.com/api/member/System.Web.Security.Membership.GeneratePassword(System.Int32,System.Int32)/。 - Pooran
我想到了这一点,但是无法摆脱非字母数字字符,因为第二个参数是最小的非字母数字字符。 - Andy

15

我的简单一行代码对我有效 :)

string  random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));

Response.Write(random.ToUpper());
Response.Write(random.ToLower());

为了对任意长度的字符串进行拓展

    public static string RandomString(int length)
    {
        //length = length < 0 ? length * -1 : length;
        var str = "";

        do 
        {
            str += Guid.NewGuid().ToString().Replace("-", "");
        }

        while (length > str.Length);

        return str.Substring(0, length);
    }

1
我也喜欢GUID方法 - 感觉非常轻巧。 - Andy
3
对于一个5位字符,这只需要0毫秒,但被接受的答案需要2毫秒。既然有Guid类可用,为什么要重新发明轮子呢 :)改进您的答案应该是使用.ToString("N")来移除连字符。 - Tom McDonough

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