[C#] 我如何在C#中生成隨機字母數字字符串?


Answers

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類。)

Question

如何在C#中生成隨機的8個字符的字母數字字符串?




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

您的風格偏好可能有所不同




我的代碼的主要目標是:

  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];
    new RNGCryptoServiceProvider().GetBytes(bytes);
    var result = new char[length];
    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);
}



另一種選擇是使用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());






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



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



Eric J.編寫的代碼很sl((很明顯,它是從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);
    }
}



解決方案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,與其他解決方案相比,它需要更多的字符來表示相同的範圍。

這意味著更多的碰撞機會 - 用100,000個重複的8個字符串測試它,生成一個重複。




很簡單的解決方案 它使用ASCII值並在它們之間生成“隨機”字符。

public static class UsernameTools
{
    public static string GenerateRandomUsername(int length = 10)
    {
        Random random = new Random();
        StringBuilder sbuilder = new StringBuilder();
        for (int x = 0; x < length; ++x)
        {
            sbuilder.Append((char)random.Next(33, 126));
        }
        return sbuilder.ToString();
    }

}



可怕的是,我知道,但我無法自拔:


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




A solution without using Random :

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

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



I don't know how cryptographically sound this is, but it's more readable and concise than the more intricate solutions by far (imo), and it should be more "random" than System.Random -based solutions.

return alphabet
    .OrderBy(c => Guid.NewGuid())
    .Take(strLength)
    .Aggregate(
        new StringBuilder(),
        (builder, c) => builder.Append(c))
    .ToString();

I can't decide if I think this version or the next one is "prettier", but they give the exact same results:

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

Granted, it isn't optimized for speed, so if it's mission critical to generate millions of random strings every second, try another one!

NOTE: This solution doesn't allow for repetitions of symbols in the alphabet, and the alphabet MUST be of equal or greater size than the output string, making this approach less desirable in some circumstances, it all depends on your use-case.




我正在尋找一個更具體的答案,我想控制隨機字符串的格式,並遇到這篇文章。 例如:(汽車)車牌具有特定的格式(每個國家),我想創建隨機車牌。
我決定為此編寫我自己的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://.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)];
    }
}



I know this one is not the best way. But you can try this.

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