ChatGPT解决这个技术问题 Extra ChatGPT

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

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

如果有的话,你对字符集有什么限制?只有英文字符和0-9?混合情况?
请注意,您不应使用任何基于 Random 类的方法来生成密码。 Random 的种子具有非常低的熵,因此它不是很安全。对密码使用加密 PRNG。
在这个问题中包含语言本地化会很好。特别是如果您的 gui 需要迎合中国人或保加利亚人的需求!
有这么多赞和这么多高质量答案的东西不应该被标记为已关闭。我投票决定重新开放。

W
Wai Ha Lee

我听说 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 类.)


@Alex:我已经运行了一些快速测试,并且在生成更长的字符串时它似乎几乎可以线性扩展(只要实际上有足够的可用内存)。话虽如此,丹·里格比的答案在每次测试中几乎是这个答案的两倍。
出色地。如果您的标准是它使用 linq 并且它的代码叙述很糟糕,那么它绝对是蜜蜂的膝盖。代码叙述和实际执行路径都相当低效和间接。不要误会我的意思,我是一个巨大的代码潮人(我喜欢 python),但这几乎是一个 rube goldberg 机器。
虽然这在技术上回答了这个问题,但它的输出非常具有误导性。生成 8 个随机字符听起来可能有很多结果,而这最多会产生 20 亿个不同的结果。在实践中甚至更少。您还应该添加一个 BIG FAT 警告,不要将其用于任何与安全相关的东西。
@xaisoft:小写字母留给读者练习。
以下行比给定的 return new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray()); 更节省内存(因此更节省时间)
T
TylerH
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 类.)


@Alex:这不是绝对最快的答案,但它是最快的“真实”答案(即,允许控制所使用的字符和字符串长度的答案)。
@Alex:Adam Porad 的 GetRandomFileName 解决方案更快,但不允许对使用的字符进行任何控制,最大可能长度为 11 个字符。 Douglas 的 Guid 解决方案速度极快,但字符限制为 A-F0-9,最大可能长度为 32 个字符。
@Adam:是的,您可以连接多次调用 GetRandomFileName 的结果,但是(a)您会失去性能优势,并且(b)您的代码会变得更加复杂。
@xaisoft 在循环之外创建 Random() 对象的实例。如果您在短时间内创建大量 Random() 实例,则对 .Next() 的调用将返回与 Random() 使用基于时间的种子相同的值。
@xaisoft 不要将此答案用于任何安全关键,例如密码。 System.Random 不适合安全性。
E
Eric J.

已针对 .NET 6 更新。RNGCryptoServiceProvider 被标记为已过时。相反,调用 RandomNumberGenerator.Create()。答案中的代码已相应更新。

根据评论更新。原始实现生成 ah ~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();
        }
    }
}

基于对备选方案 here 的讨论,并根据以下评论进行了更新/修改。

这是一个小型测试工具,演示了旧输出和更新输出中的字符分布。如需深入讨论 analysis of randomness,请访问 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")}%");
            }
        }
    }
}

这对我来说似乎是正确的方法——不应该使用 Random() 生成随机密码、盐、熵等,它针对速度进行了优化并生成可重现的数字序列;另一方面,RNGCryptoServiceProvider.GetNonZeroBytes() 会产生不可重现的狂野数字序列。
字母略有偏差(255 % 62 != 0)。尽管有这个小缺陷,但它是迄今为止最好的解决方案。
请注意,如果您想要加密强度、无偏随机性,这不是声音。 (如果您不希望这样做,那么为什么首先使用 RNGCSP?)使用 mod 索引到 chars 数组意味着您将得到有偏差的输出,除非 chars.Length 恰好是 256 的除数.
一种大幅减少偏差的可能性是请求 4*maxSize 个随机字节,然后使用 (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length。我也会使用 GetBytes 而不是 GetNonZeroBytes。最后,您可以删除对 GetNonZeroBytes 的第一次调用。你没有使用它的结果。
有趣的事实:AZ az 0-9 是 62 个字符。人们指出字母偏差是因为 256 % 62 != 0。YouTube 的视频 ID 是 AZ az 0-9,以及“-”和“_”,它们产生 64 个可能的字符,平均分为 256 个。巧合?我想不是! :)
L
Luis Perez

解决方案 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 次迭代测试它会生成一个重复项。


您实际上生成了一个副本?令人惊讶的是 5,316,911,983,139,663,491,615,228,241,121,400,000 种可能的 GUID 组合。
@Alex:他将 GUID 缩短为 8 个字符,因此发生冲突的概率远高于 GUID。
除了书呆子,没有人能理解这一点:) 是的,你是绝对正确的,8 个字符的限制会有所不同。
Guid.NewGuid().ToString("n") 将保留破折号,不需要 Replace() 调用。但应该提到的是,GUID 只有 0-9 和 AF。组合的数量“足够好”,但远不及真正的字母数字随机字符串所允许的数量。碰撞的几率是 1:4,294,967,296——与随机 32 位整数相同。
1) GUID 被设计为唯一的,而不是随机的。虽然当前版本的 Windows 生成的 V4 GUID 确实是随机的,但这并不能保证。例如,旧版本的 Windows 使用 V1 GUID,您可能会失败。 2) 仅使用十六进制字符会显着降低随机字符串的质量。从 47 位到 32 位。 3)人们低估了碰撞概率,因为他们给了个体对。如果您生成 100k 32 位值,则它们之间可能会发生一次冲突。请参阅生日问题。
d
dtb

这是我从 Dot Net Perls 的 Sam Allen 示例中窃取的示例

如果您只需要 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
}

PS:谢谢山姆


这很好用。我对其进行了 100,000 次迭代,并且从未有过重复的名称。但是,我确实找到了几个粗俗的单词(英文)。甚至不会想到这一点,除非列表中的第一个有 F*** 在里面。如果您将其用于用户将看到的内容,请注意。
@techturtle 感谢您的警告。我想任何使用字母表中所有字母的随机字符串生成都存在粗俗单词的风险。
又好又简单,但不适合长字符串……投票给这个好技巧
此方法似乎只返回小写字母数字字符串。
时不时会出现一些粗俗的词,但如果你保持它运行足够长的时间,最终它会写出莎士比亚。 (只是宇宙的几个生命周期。:)
p
piojo

我的代码的主要目标是:

字符串的分布几乎是均匀的(不关心微小的偏差,只要它们很小)它为每个参数集输出超过几十亿个字符串。如果您的 PRNG 只生成 20 亿(31 位熵)不同的值,那么生成 8 个字符串(约 47 位熵)是没有意义的。它是安全的,因为我希望人们将它用于密码或其他安全令牌。

第一个属性是通过以字母大小为模取一个 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);
}

64 x Z 和 Math.Pow(2,Y) 没有交集。因此,虽然制作更大的数字可以减少偏差,但并不能消除它。我在下面更新了我的答案,我的方法是丢弃随机输入并用另一个值替换。
@Todd 我知道它并不能消除偏见,但我选择了这个解决方案的简单性,而不是消除几乎不相关的偏见。
我同意在大多数情况下它可能实际上是无关紧要的。但是现在我已经将我的更新为与 Random 一样快,并且比你的更安全。全部开源,供大家分享。是的,我在这上面浪费了太多时间……
如果我们使用 RNG 提供者,我们有什么方法可以避免理论上的偏见吗?我不确定......如果托德是指他生成额外随机数的方式(当我们处于偏差区域时),那么这可能是错误的假设。 RNG 平均具有所有生成值的几乎线性分布。但这并不意味着我们不会在生成的字节之间存在局部相关性。因此,仅针对偏差区域的附加字节仍然可以给我们一些偏差,但由于不同的原因。这种偏差很可能非常小。但在这种情况下,增加总生成字节数是更直接的方法。
@Maxim您可以使用拒绝来完全消除偏差(假设底层生成器是完全随机的)。作为交换,代码可能会运行任意长(具有指数级小概率)。
n
nawfal

最简单的:

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

如果您对 char 数组进行硬编码并依赖 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 方法相比)

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 实例上的扩展方法,它们看起来会更好。


使用 chars.Select 非常难看,因为它依赖于输出大小最多为字母大小。
@CodesInChaos 我不确定我是否理解你。您的意思是在 'a'.To('z') 方法中?
1) chars.Select().Take(n)` 仅在 chars.Count >= n 时有效。选择您实际上不使用的序列有点不直观,尤其是在隐含长度约束的情况下。我宁愿使用 Enumerable.RangeEnumerable.Repeat。 2) 错误消息“结束字符应小于开始字符”是错误的方式/缺少 not
@CodesInChaos 但在我的情况下,chars.Count> n。我也没有得到不直观的部分。这确实使所有使用 Take 不直观,不是吗?我不相信。感谢您指出错字。
这是在 theDailyWTF.com 上作为 CodeSOD 文章的特色。
K
Kind Contributor

只是这个线程中各种答案的一些性能比较:

方法和设置

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


// http://stackoverflow.com/a/1344242/1037948
public string LinqIsTheNewBlack(int num) {
    return new string(
    Enumerable.Repeat(possibleCharsArray, num)
              .Select(s => s[random.Next(s.Length)])
              .ToArray());
}

// http://stackoverflow.com/a/1344258/1037948
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());
}

// http://stackoverflow.com/a/1518495/1037948
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] 从 Loop = gtnoaryhxr [10] 从 Select = rsndbztyby [10] 从 GenerateRandomString = owyefjjakj [10] 从 SecureFastRandom = VzougLYHYP [10] 从 SecureFastRandom-NoCache = oVQXNGmO1S [10]

并且性能数字往往略有不同,有时 NonOptimized 实际上更快,有时 ForLoopGenerateRandomString 切换谁领先。

LinqIsTheNewBlack (10000x) = 96762 个滴答经过 (9.6762 毫秒) ForLoop (10000x) = 28970 个滴答经过 (2.897 毫秒) ForLoopNonOptimized (10000x) = 33336 个滴答经过 (3.3336 毫秒) 重复 (10000x) = 78547 个滴答经过 (78.8 生成随机字符串) 10000x) = 27416 个滴答经过 (2.7416 ms) SecureFastRandom (10000x) = 13176 个滴答经过 (5ms) 最低 [不同机器] SecureFastRandom-NoCache (10000x) = 39541 个滴答经过 (17ms) 最低 [不同机器]


知道哪些人创造了骗局会很有趣。
@Junto - 找出导致重复的结果,例如 var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());,您将 EachRandomizingMethod 替换为...每个方法
S
Stephen Kennedy

一行代码 Membership.GeneratePassword() 就可以解决问题:)

这是相同的demo


我想到了这一点,但无法摆脱非字母数字字符,因为第二个参数是 MINIMUM 非字母字符
x
xanatos

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

与提出的一些替代方案不同,这个方案在密码学上是可靠的。

不正确...密码存在偏差(如评论中所写),bcdefgh 比其他密码更可能(a 不是因为 GetNonZeroBytes 它没有生成字节值为零,因此 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);
    }
}

A
Andy

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

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

我也喜欢引导方法 - 感觉很轻
对于 5 位字符,这需要 0 毫秒,但接受的答案需要 2 毫秒。当你有一个 Guid 类时为什么要重新发明轮子:) 改进你的答案应该是 .ToString("N") 删除连字符
M
Mr. Pumpkin

我们还使用自定义字符串随机,但我们实现的是作为字符串的助手,因此它提供了一些灵活性......

public static string Random(this string chars, int length = 8)
{
    var randomString = new StringBuilder();
    var random = new Random();

    for (int i = 0; i < length; i++)
        randomString.Append(chars[random.Next(chars.Length)]);

    return randomString.ToString();
}

用法

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();

或者

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);

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

A
AAD

另一种选择可能是使用 Linq 并将随机字符聚合到字符串生成器中。

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

W
Wai Ha Lee

问题:我为什么要浪费时间使用 Enumerable.Range 而不是输入 "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 之类的许多其他人所说,如果您需要加密安全,请不要使用 System.Random ...

nb 2:这个答案不是最有效或最短的,但我希望空间将答案与问题分开。我回答的目的更多的是警告魔术字符串,而不是提供一个花哨的创新答案。


为什么我关心没有“I”?
字母数字(忽略大小写)为 [A-Z0-9]。如果意外地,您的随机字符串只涵盖 [A-HJ-Z0-9],则结果未涵盖完整的允许范围,这可能会出现问题。
怎么会有问题?所以它不包含 I。是不是因为少了一个字符,更容易破解?包含 35 个字符而不是 36 个字符的可破解密码的统计数据是什么。我想我宁愿冒险……或者只是证明字符范围……而不是在我的代码中包含所有额外的垃圾。但是,这就是我。我的意思是,不要成为一个屁股洞,我只是说。有时我认为程序员倾向于为了更复杂而走更复杂的路线。
它加深了用例。从这些类型的随机字符串中排除 IO 等字符是很常见的,以避免人们将它们与 10 混淆。如果您不关心人类可读的字符串,那很好,但是如果有人可能需要输入它,那么删除这些字符实际上是明智的。
R
Rob Deary

DTB 解决方案的稍微干净的版本。

    var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    var random = new Random();
    var list = Enumerable.Repeat(0, 8).Select(x=>chars[random.Next(chars.Length)]);
    return string.Join("", list);

您的风格偏好可能会有所不同。


这比公认的答案更好,更有效。
K
Kind Contributor

在查看了其他答案并考虑了 CodeInChaos 的评论以及 CodeInChaos 仍然有偏见(尽管较少)的答案之后,我认为需要最终的最终剪切和粘贴解决方案。因此,在更新我的答案时,我决定全力以赴。

有关此代码的最新版本,请访问 Bitbucket 上的新 Hg 存储库:https://bitbucket.org/merarischroeder/secureswiftrandom。我建议您从 https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default 复制并粘贴代码(确保您单击 Raw 按钮以便于复制并确保您拥有最新版本,我认为此链接指向特定版本的代码,而不是最新的)。

更新说明:

与其他一些答案有关 - 如果您知道输出的长度,则不需要 StringBuilder,并且在使用 ToCharArray 时,这会创建并填充数组(您不需要先创建空数组)其他答案 - 您应该使用 NextBytes,而不是一次获得一个以提高性能 从技术上讲,您可以固定字节数组以获得更快的访问速度。当您在字节数组上迭代超过 6-8 次时,通常是值得的。 (此处未完成)使用 RNGCryptoServiceProvider 获得最佳随机性 使用 1MB 随机数据缓冲区的缓存 - 基准测试显示缓存的单字节访问速度快约 1000 倍 - 在 1MB 上花费 9 毫秒,而未缓存则为 989 毫秒。在我的新班级中优化了对偏见区域的拒绝。

结束问题的解决方案:

static char[] charSet =  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
public string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
    char[] rName = new char[Length];
    SecureFastRandom.GetNextBytesMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

但是您需要我的新(未经测试)课程:

/// <summary>
/// My benchmarking showed that for RNGCryptoServiceProvider:
/// 1. There is negligable benefit of sharing RNGCryptoServiceProvider object reference 
/// 2. Initial GetBytes takes 2ms, and an initial read of 1MB takes 3ms (starting to rise, but still negligable)
/// 2. Cached is ~1000x faster for single byte at a time - taking 9ms over 1MB vs 989ms for uncached
/// </summary>
class SecureFastRandom
{
    static byte[] byteCache = new byte[1000000]; //My benchmark showed that an initial read takes 2ms, and an initial read of this size takes 3ms (starting to raise)
    static int lastPosition = 0;
    static int remaining = 0;

    /// <summary>
    /// Static direct uncached access to the RNGCryptoServiceProvider GetBytes function
    /// </summary>
    /// <param name="buffer"></param>
    public static void DirectGetBytes(byte[] buffer)
    {
        using (var r = new RNGCryptoServiceProvider())
        {
            r.GetBytes(buffer);
        }
    }

    /// <summary>
    /// Main expected method to be called by user. Underlying random data is cached from RNGCryptoServiceProvider for best performance
    /// </summary>
    /// <param name="buffer"></param>
    public static void GetBytes(byte[] buffer)
    {
        if (buffer.Length > byteCache.Length)
        {
            DirectGetBytes(buffer);
            return;
        }

        lock (byteCache)
        {
            if (buffer.Length > remaining)
            {
                DirectGetBytes(byteCache);
                lastPosition = 0;
                remaining = byteCache.Length;
            }

            Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
            lastPosition += buffer.Length;
            remaining -= buffer.Length;
        }
    }

    /// <summary>
    /// Return a single byte from the cache of random data.
    /// </summary>
    /// <returns></returns>
    public static byte GetByte()
    {
        lock (byteCache)
        {
            return UnsafeGetByte();
        }
    }

    /// <summary>
    /// Shared with public GetByte and GetBytesWithMax, and not locked to reduce lock/unlocking in loops. Must be called within lock of byteCache.
    /// </summary>
    /// <returns></returns>
    static byte UnsafeGetByte()
    {
        if (1 > remaining)
        {
            DirectGetBytes(byteCache);
            lastPosition = 0;
            remaining = byteCache.Length;
        }

        lastPosition++;
        remaining--;
        return byteCache[lastPosition - 1];
    }

    /// <summary>
    /// Rejects bytes which are equal to or greater than max. This is useful for ensuring there is no bias when you are modulating with a non power of 2 number.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    public static void GetBytesWithMax(byte[] buffer, byte max)
    {
        if (buffer.Length > byteCache.Length / 2) //No point caching for larger sizes
        {
            DirectGetBytes(buffer);

            lock (byteCache)
            {
                UnsafeCheckBytesMax(buffer, max);
            }
        }
        else
        {
            lock (byteCache)
            {
                if (buffer.Length > remaining) //Recache if not enough remaining, discarding remaining - too much work to join two blocks
                    DirectGetBytes(byteCache);

                Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
                lastPosition += buffer.Length;
                remaining -= buffer.Length;

                UnsafeCheckBytesMax(buffer, max);
            }
        }
    }

    /// <summary>
    /// Checks buffer for bytes equal and above max. Must be called within lock of byteCache.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    static void UnsafeCheckBytesMax(byte[] buffer, byte max)
    {
        for (int i = 0; i < buffer.Length; i++)
        {
            while (buffer[i] >= max)
                buffer[i] = UnsafeGetByte(); //Replace all bytes which are equal or above max
        }
    }
}

对于历史-我对此答案的旧解决方案使用了 Random 对象:

    private static char[] charSet =
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();

    static rGen = new Random(); //Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.
    static int byteSize = 256; //Labelling convenience
    static int biasZone = byteSize - (byteSize % charSet.Length);
    static bool SlightlyMoreSecurityNeeded = true; //Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.
    public string GenerateRandomString(int Length) //Configurable output string length
    {
      byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
      char[] rName = new char[Length];
      lock (rGen) //~20-50ns
      {
          rGen.NextBytes(rBytes);

          for (int i = 0; i < Length; i++)
          {
              while (SlightlyMoreSecurityNeeded && rBytes[i] >= biasZone) //Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there's still a bias on index 0.
                  rBytes[i] = rGen.NextByte();
              rName[i] = charSet[rBytes[i] % charSet.Length];
          }
      }
      return new string(rName);
    }

表现:

SecureFastRandom - 第一次运行 = ~9-33ms。难以察觉。进行中:5 毫秒(有时达到 13 毫秒)超过 10,000 次迭代,单次平均迭代 = 1.5 微秒。注意:通常需要 2 次,但偶尔最多需要 8 次缓存刷新 - 取决于有多少单字节超出偏置区域随机- 第一次运行 = ~0-1ms。难以察觉。进行中:超过 10,000 次迭代 5 毫秒。单次平均迭代 = 0.5 微秒.. 大约相同的速度。

另请查看:

https://bitbucket.org/merarischroeder/number-range-with-no-bias/src

https://stackoverflow.com/a/45118325/887092

这些链接是另一种方法。缓冲可以添加到这个新的代码库中,但最重要的是探索不同的方法来消除偏差,并对速度和优缺点进行基准测试。


我发现您的方法有一些轻微的性能增强,这似乎是最受不了的——stackoverflow.com/a/17092645/1037948
1)为什么所有这些神奇的常数?您指定了输出长度 次。只需将其定义为常量或参数即可。您可以使用 charSet.Length 代替 62。 2) 没有锁定的静态 Random 意味着此代码不是线程安全的。 3) 减少 0-255 mod 62 引入了可检测的偏差。 4) 不能在 char 数组上使用 ToString,它总是返回 "System.Char[]"。您需要改用 new String(rName)
感谢@CodesInChaos,那时我从未想过这些事情。仍然只使用 Random 类,但这应该会更好。我想不出任何更好的方法来检测和纠正偏差输入。
从一个弱 RNG (System.Random) 开始,然后小心地避免在您自己的代码中出现任何偏差,这有点愚蠢。我想到了“抛光粪便”这个表达。
@CodesInChaos 现在学徒已经超越了他的师父
S
Sam Vanhoutte

我一直在寻找更具体的答案,我想控制随机字符串的格式并遇到了这篇文章。例如:车牌(汽车)具有特定格式(每个国家/地区),我想创建随机车牌。
我决定为此编写自己的 Random 扩展方法。 (这是为了重用相同的 Random 对象,因为您可以在多线程场景中使用双打)。我创建了一个要点 (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 http://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings-in-c
        // 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)];
    }
}

M
Matas Vaitkevicius

现在是单线风味。

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

将属性用于每次访问都会更改的内容是相当可疑的。我建议改用一种方法。
RNGCryptoServiceProvider 应在使用后丢弃。
我修复了 IDisposable 问题,但这仍然非常可疑,为每个字母创建一个新的 RNGCryptoServiceProvider。
M
Mohsen Esmailpour

尝试结合两个部分:唯一(序列、计数器或日期)和随机

public class RandomStringGenerator
{
    public static string Gen()
    {
        return ConvertToBase(DateTime.UtcNow.ToFileTimeUtc()) + GenRandomStrings(5); //keep length fixed at least of one part
    }

    private static string GenRandomStrings(int strLen)
    {
        var result = string.Empty;

        using (var gen = new RNGCryptoServiceProvider())
        {
            var data = new byte[1];

            while (result.Length < strLen)
            {
                gen.GetNonZeroBytes(data);
                int code = data[0];
                if (code > 48 && code < 57 || // 0-9
                    code > 65 && code < 90 || // A-Z
                    code > 97 && code < 122   // a-z
                )
                {
                    result += Convert.ToChar(code);
                }
            }

            return result;
        }
    }

    private static string ConvertToBase(long num, int nbase = 36)
    {
        const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //if you wish to make the algorithm more secure - change order of letter here

        // check if we can convert to another base
        if (nbase < 2 || nbase > chars.Length)
            return null;

        int r;
        var newNumber = string.Empty;

        // in r we have the offset of the char that was converted to the new base
        while (num >= nbase)
        {
            r = (int)(num % nbase);
            newNumber = chars[r] + newNumber;
            num = num / nbase;
        }
        // the last number to convert
        newNumber = chars[(int)num] + newNumber;

        return newNumber;
    }
}

测试:

    [Test]
    public void Generator_Should_BeUnigue1()
    {
        //Given
        var loop = Enumerable.Range(0, 1000);
        //When
        var str = loop.Select(x=> RandomStringGenerator.Gen());
        //Then
        var distinct = str.Distinct();
        Assert.AreEqual(loop.Count(),distinct.Count()); // Or Assert.IsTrue(distinct.Count() < 0.95 * loop.Count())
    }

1) 您可以使用字符文字而不是与这些字符关联的 ASCII 值。 2)您的区间匹配代码中有一个错误。您需要使用 <=>= 而不是 <>。 3) 我会在 && 表达式周围添加不必要的括号,以明确它们具有优先级,但这当然只是一种风格选择。
+ 1 有利于消除偏差和添加测试。我不确定你为什么在你的随机字符串前面加上一个时间戳派生的字符串?此外,您仍然需要处理您的 RNGCryptoServiceProvider
J
Jake Walden

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

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

M
Misha Zaslavsky

一种简单且高度安全的方法可能是生成加密 Aes 密钥。

public static string GenerateRandomString()
{
    using Aes crypto = Aes.Create();
    crypto.GenerateKey();
    return Convert.ToBase64String(crypto.Key);
}

j
james

可怕,我知道,但我就是忍不住:


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

w
w.b

不使用 Random 的解决方案:

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

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

NewGuid 在内部使用随机。所以这仍然使用随机,它只是隐藏它。
h
huyc

这是 Eric J 解决方案的一个变体,即 WinRT(Windows 应用商店应用程序)的加密声音:

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

这在密码学上不是合理的。由于模运算没有将 ulong 的整个宽度均匀地分布到 62 个字符中,因此存在一个小的偏差。
S
Sagar

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

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

那一行怎么样? Console.WriteLine($"随机字符串:{Path.GetRandomFileName().Replace(".","")}");是一条线。
s
sara

我不知道这在密码学上听起来如何,但它比迄今为止更复杂的解决方案 (imo) 更具可读性和简洁性,并且它应该比基于 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());

当然,它没有针对速度进行优化,所以如果每秒生成数百万个随机字符串是关键任务,请尝试另一个!

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


A
Alexey B.

如果您的值不是完全随机的,但实际上可能取决于某些东西 - 您可以计算该“somwthing”的 md5 或 sha1 哈希,然后将其截断为您想要的任何长度。

您也可以生成和截断 guid。


K
KregHEk
public static class StringHelper
{
    private static readonly Random random = new Random();

    private const int randomSymbolsDefaultCount = 8;
    private const string availableChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    private static int randomSymbolsIndex = 0;

    public static string GetRandomSymbols()
    {
        return GetRandomSymbols(randomSymbolsDefaultCount);
    }

    public static string GetRandomSymbols(int count)
    {
        var index = randomSymbolsIndex;
        var result = new string(
            Enumerable.Repeat(availableChars, count)
                      .Select(s => {
                          index += random.Next(s.Length);
                          if (index >= s.Length)
                              index -= s.Length;
                          return s[index];
                      })
                      .ToArray());
        randomSymbolsIndex = index;
        return result;
    }
}

1)静态方法应该是线程安全的。 2)增加索引而不是直接使用 random.Next 的结果有什么意义?使代码复杂化并且没有实现任何有用的东西。
D
Dhanuka777

这是一种生成随机字母数字字符串的机制(我用它来生成密码和测试数据)而不定义字母和数字,

CleanupBase64 将删除字符串中的必要部分并继续递归地添加随机字母数字字母。

        public static string GenerateRandomString(int length)
        {
            var numArray = new byte[length];
            new RNGCryptoServiceProvider().GetBytes(numArray);
            return CleanUpBase64String(Convert.ToBase64String(numArray), length);
        }

        private static string CleanUpBase64String(string input, int maxLength)
        {
            input = input.Replace("-", "");
            input = input.Replace("=", "");
            input = input.Replace("/", "");
            input = input.Replace("+", "");
            input = input.Replace(" ", "");
            while (input.Length < maxLength)
                input = input + GenerateRandomString(maxLength);
            return input.Length <= maxLength ?
                input.ToUpper() : //In my case I want capital letters
                input.ToUpper().Substring(0, maxLength);
        }

您已声明 GenerateRandomString 并从 SanitiseBase64String 中调用 GetRandomString。您还声明了 SanitiseBase64String 并在 GenerateRandomString 中调用了 CleanUpBase64String